Get campaign statistics
This is a v1 legacy endpoint. It uses a different path /rest/v1
and may return different error codes and response formats compared to v2. While it remains functional, consider handling errors accordingly.
Retrieve campaign statistics. Use this endpoint to analyze the performance of a specific cold email campaign, including delivery, open, reply, bounce, and opt-out rates.
Understanding the response:
- Step versions - The response includes only the A version for email content and step settings, while performance metrics reflect data from all versions combined
- Tasks - Not supported by this endpoint, they are omitted from the response
If you're looking for:
- More campaign statistics - supplement your data with predefined reports
- A detailed campaign structure including all campaign settings - see
v2/campaigns/{id}
endpoint here - A list of all campaigns - see
v1/campaign_list
endpoint here
Request
Endpoint
GET https://api.woodpecker.co/rest/v1/campaign_list?id={campaign_id}
Headers
x-api-key: {YOUR_API_KEY}
For details on how to authenticate your requests, please see the authentication guide.
Parameters
Parameter | Required | Description |
---|---|---|
id | Yes | Specify a single campaign id to retrieve detailed campaign information. Using multiple IDs returns a list of campaigns without statistics. |
Request sample
Fetch a campaign and its statistics
- cURL
- Java
- Node.js
curl --request GET \
--url "https://api.woodpecker.co/rest/v1/campaign_list?id={campaign_id}" \
--header "x-api-key: {YOUR_API_KEY}"
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class WoodpeckerApiClient {
public static void main(String[] args) {
String apiKey = "YOUR_API_KEY";
String url = "https://api.woodpecker.co/rest/v1/campaign_list?id={campaign_id}";
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("x-api-key", apiKey)
.GET()
.build();
try {
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println("Response: " + response.body());
} catch (Exception e) {
e.printStackTrace();
}
}
}
const axios = require("axios");
const apiKey = "YOUR_API_KEY";
const url = "https://api.woodpecker.co/rest/v1/campaign_list?id={campaign_id}";
async function fetchCampaignStats() {
try {
const response = await axios.get(url, {
headers: {
"x-api-key": apiKey,
},
});
console.log("Response:", response.data);
} catch (error) {
console.error("Error fetching campaign statistics:", error.response ? error.response.data : error.message);
}
}
fetchCampaignStats();
Response
Response examples
- 200
- 204
- 400
- 401
- 403
- 404
- 409
- 500
An array of all campaigns meeting your criteria.
[
{
"id": 1234567,
"name": "SQL follow-ups",
"status": "RUNNING",
"folder_name": "SaaS in America",
"folder_id": 987,
"from_name": "Erlich Bachman",
"from_names": ["Erlich Bachman", "Jared Dunn", "Richard Hendricks", "Jian"],
"gdpr_unsubscribe": true,
"created": "2025-02-10T13:14:57+0100",
"per_day": 70,
"from_email": "erlich.bachman@piedpiper.com",
"from_emails": ["erlich.bachman@piedpiper.com", "jared.dunn@piedpiper.com", "richard.hendricks@piedpiper.com", "jian@bachmanity.com"],
"bcc": "sentemails@crm.com",
"cc": "",
"stats": {
"prospects": 868,
"delivery": 853,
"invalid": 6,
"bounced": 4,
"queue": 3,
"sent": 857,
"check": 21,
"autoreplied": 0,
"opened": 286,
"optout": 3,
"clicked": 0,
"replied": 74,
"interested": 42,
"maybe_later": 11,
"not_interested": 13,
"emails": [
{
"subject": "Example subject line",
"msg": "<div><p>Hi {{FIRST_NAME | \"there\"}},</p><p>This is an example cold email message.</p><p>Best wishes,<br /><a href=\"https://woodpecker.co\">Woodpecker</a> team</p></div>",
"timezone": "Europe/Warsaw",
"use_prospect_timezone": false,
"sunFrom": null,
"sunTo": null,
"monFrom": null,
"monTo": null,
"tueFrom": null,
"tueTo": null,
"wedFrom": null,
"wedTo": null,
"thuFrom": null,
"thuTo": null,
"friFrom": null,
"friTo": null,
"satFrom": null,
"satTo": null,
"sunday": [{ "from": -1, "to": -1 }],
"monday": [{ "from": 360, "to": 720 }, { "from": 900, "to": 990 }],
"tuesday": [{ "from": 360, "to": 720 }, { "from": 900, "to": 990 }],
"wednesday": [{ "from": 360, "to": 720 }],
"thursday": [{ "from": 360, "to": 720 }],
"friday": [{ "from": 360, "to": 720 }],
"saturday": [{ "from": -1, "to": -1 }],
"track_open": true,
"track_click": false,
"attach_follow": false,
"follow_up": 0,
"number": 1,
"step": 1,
"condition": null,
"emailSend": 857,
"toSend": 3,
"delivery": 854,
"open_": "29.3%",
"open": 250,
"reply_": "2.9%",
"reply": 25,
"invalid_": "0.0%",
"invalid": 0,
"bounce_": "0.4%",
"bounce": 3
},
{
"subject": "Re: Example subject line",
"msg": "<div>First followup</div>",
"timezone": "Europe/Warsaw",
"use_prospect_timezone": false,
"sunFrom": null,
"sunTo": null,
"monFrom": null,
"monTo": null,
"tueFrom": null,
"tueTo": null,
"wedFrom": null,
"wedTo": null,
"thuFrom": null,
"thuTo": null,
"friFrom": null,
"friTo": null,
"satFrom": null,
"satTo": null,
"sunday": [{ "from": -1, "to": -1 }],
"monday": [{ "from": 360, "to": 720 }, { "from": 900, "to": 990 }],
"tuesday": [{ "from": 360, "to": 720 }, { "from": 900, "to": 990 }],
"wednesday": [{ "from": 360, "to": 720 }],
"thursday": [{ "from": 360, "to": 720 }],
"friday": [{ "from": 360, "to": 720 }],
"saturday": [{ "from": -1, "to": -1 }],
"track_open": false,
"track_click": false,
"attach_follow": false,
"follow_up": 0,
"number": 3,
"step": 2,
"condition": {
"operator": "",
"values": [
{
"type": "PROSPECT_FIRST_NAME",
"operand": "EXISTS",
"value": ""
}
]
},
"emailSend": 818,
"toSend": 10,
"delivery": 818,
"open_": "24.1%",
"open": 197,
"reply_": "3.4%",
"reply": 28,
"invalid_": "0.0%",
"invalid": 0,
"bounce_": "0.0%",
"bounce": 0
},
{
"subject": "Re: Example subject line",
"msg": "<div>Second followup</div>",
"timezone": "Europe/Warsaw",
"use_prospect_timezone": false,
"sunFrom": null,
"sunTo": null,
"monFrom": null,
"monTo": null,
"tueFrom": null,
"tueTo": null,
"wedFrom": null,
"wedTo": null,
"thuFrom": null,
"thuTo": null,
"friFrom": null,
"friTo": null,
"satFrom": null,
"satTo": null,
"sunday": [{ "from": -1, "to": -1 }],
"monday": [{ "from": 360, "to": 720 }, { "from": 900, "to": 990 }],
"tuesday": [{ "from": 360, "to": 720 }, { "from": 900, "to": 990 }],
"wednesday": [{ "from": 360, "to": 720 }],
"thursday": [{ "from": 360, "to": 720 }],
"friday": [{ "from": 360, "to": 720 }],
"saturday": [{ "from": -1, "to": -1 }],
"track_open": false,
"track_click": false,
"attach_follow": false,
"follow_up": 0,
"number": 7,
"step": 3,
"condition": null,
"emailSend": 546,
"toSend": 25,
"delivery": 545,
"open_": "16.0%",
"open": 87,
"reply_": "4.0%",
"reply": 22,
"invalid_": "0.0%",
"invalid": 0,
"bounce_": "0.2%",
"bounce": 1
}
]
},
"error": "",
"timestamp": "2025-03-01T15:00:30+0100"
}
]
Body schema
Field | Data Type | Description |
---|---|---|
id | integer | Unique identifier of the campaign |
name | string | Name of the campaign |
status | string | Current campaign status. Possible values: RUNNING , DRAFT , STOPPED , PAUSED , EDITED , COMPLETED |
folder_name | string | Name of the folder the campaign is assigned to |
folder_id | integer | ID of the folder the campaign is assigned to. 0 stands for general UNASSIGNED folder |
from_name | string | One of the sending emails 'from name'. If multiple are used, refer to from_names instead |
from_names | array[string] | A list of sender names used in the campaign |
gdpr_unsubscribe | boolean | Whether GDPR-compliant unsubscribe is enabled |
created | string | Campaign creation date (ISO 8601) |
per_day | integer | Maximum number of opening emails that can be sent per day. The limit is shared between all mailboxes |
from_email | string | One of the campaign sending email addresses. If multiple are used, refer to from_emails instead |
from_emails | array[string] | List of campaign sending email addresses |
bcc | string | Email address that receives a blind copy of outgoing messages |
cc | string | Email address that receives a carbon copy of outgoing messages |
stats | object | Object holding campaign-level statistics |
└─prospects | integer | Number of prospects added to the campaign |
└─delivery | integer | Number of emails successfully delivered opening emails |
└─invalid | integer | Number of invalid email addresses |
└─bounced | integer | Number of prospects marked as BOUNCED |
└─queue | integer | Number of prospects queued to receive the opening email |
└─sent | integer | Total number of contacted prospects |
└─check | integer | Number of prospects marked as to check (except manual pause) |
└─autoreplied | integer | Number of prospects marked as AUTOREPLIED |
└─opened | integer | Number of prospects who opened an email |
└─optout | integer | Number of prospects who opted out |
└─clicked | integer | Number of prospects who clicked a tracked link |
└─replied | integer | Number of prospects who replied |
└─interested | integer | Number of "interested" responses |
└─maybe_later | integer | Number of "maybe later" responses |
└─not_interested | integer | Number of "not_interested" responses |
└─emails | array[objects] | An array of email objects. Each object being the A version of a campaign steps. More details below |
[].error | deprecated | Deprecated. Empty string |
[].timestamp | string | Timestamp of sending the request (ISO 8601) |
Emails object body schema
The email object holds step-specific statistics like sent, delivered, opened, replied, bounced, and invalid email counts, along with open and reply rates. It also includes basic details like the subject line and schedule. The array order follows the campaign sequence, with the first element representing the first step.
Field | Data Type | Description |
---|---|---|
subject | string | Email subject line |
msg | string | Email body content in HTML format |
timezone | string | The default timezone of a campaign. It will be used when use_prospect_timezone is disabled or when it is enabled but the prospect's timezone is not specified |
use_prospect_timezone | boolean | Whether to adjust sending times to prospect's timezone instead of the campaign timezone |
track_open | boolean | Whether to track email opens for this email step version |
track_click | boolean | Whether this email step version contains a tracked link |
attach_follow | boolean | Deprecated. Always false |
follow_up | integer | Deprecated. Always 0 |
number | integer | Deprecated |
step | integer | Step order in the email sequence |
condition | object/null | An IF-condition that will evaluate the prospect's YES/NO path after this step |
└─operator | string | Empty string |
└─values | array[object] | Array containing condition details |
└─[].type | string | Type of condition. OPEN , CLICK , PROSPECT_{SNIPPET_NAME} |
└─[].operand | string | MORE_THAN , EXISTS , EQUALS , CONTAINS |
└─[].value | string | Prospect value that will be evaluated against the type and operand of the condition. |
emailSend | integer | Number of emails sent in this step |
toSend | integer | Number of emails remaining to be sent in this step |
delivery | integer | Number of delivered emails in this step |
open_ | string | Open rate percentage for this step (formatted as astring) |
open | integer | Number of prospects who have opened an email in this step |
reply_ | string | Reply rate percentage (formatted as astring) |
reply | integer | Number of prospects who have replied to an email from this step |
invalid_ | string | Percentage of emails marked as INVALID at this step (formatted as astring) |
invalid | integer | Number of emails marked as INVALID at this step |
bounce_ | string | Bounce rate percentage for this step (formatted as astring) |
bounce | integer | Number of bounced emails at this step |
sunFrom - satTo | null | Deprecated. Always null |
monday - sunday | array[object] | Array of sending time windows per weekday. There can be two sending windows per day |
└─[].from | integer | The start time of an email sending window, measured in minutes from midnight. Minimum 0 , maximum 1440 , -1 means no emails will be sent this day |
└─[].to | integer | The end time of an email sending window, measured in minutes from midnight. Minimum 0 , maximum 1440 , -1 means no emails will be sent this day |
There is no campaign matching your criteria
Status: 204
Body: None
Invalid request or malformed request syntax. Please review the request
{
"status": {
"status": "ERROR",
"code": "E_WRONG_PARAM",
"msg": "Wrong param [id]=campaignName" | "Unknown param:ID"
}
}
Body schema
Field | Data Type | Description |
---|---|---|
status | object | Contains error details |
└─status | string | Overall status, set to ERROR for non-2xx responses |
└─code | string | Code indicating the error category |
└─msg | string | Descriptive error message |
An issue with authorization. Please review the authorization guide
{
"status": {
"status": "ERROR",
"code": "E_SESSION",
"msg": "The API key you've entered is incorrect or no longer valid. Check if you pasted the key correctly. You can generate a new key in Woodpecker: Settings -> API Keys."
}
}
Body schema
Field | Data Type | Description |
---|---|---|
status | object | Contains error details |
└─status | string | Overall status, set to ERROR for non-2xx responses |
└─code | string | Code indicating the error category |
└─msg | string | Descriptive error message |
API access denied. You subscription might not be active, lack the API add-on, or the key belongs to an inactive client company.
{
"status": {
"status": "ERROR",
"code": "E_NO_PERMISSION",
"msg": "Api access denied." | "You need to have an API keys addon to access our API."
}
}
Body schema
Field | Data Type | Description |
---|---|---|
status | object | Contains error details |
└─status | string | Overall status, set to ERROR for non-2xx responses |
└─code | string | Code indicating the error category |
└─msg | string | Descriptive error message |
Please review the request URL
{
"status": {
"status": "ERROR",
"code": "E_URL_NOT_FOUND",
"msg": "URL not found: /Woodpecker/rest/v1/webhooks/someMadeUpURL"
}
}
Body schema
Field | Data Type | Description |
---|---|---|
status | object | Contains error details |
└─status | string | Overall status, set to ERROR for non-2xx responses |
└─code | string | Code indicating the error category |
└─msg | string | Descriptive error message |
Please review the rate limits. API v1 is subject to the same rate limits as v2, however the response code is 409
instead of 429
.
{
"status": {
"status": "ERROR",
"code": "E_TOO_MANY_REQUESTS",
"msg": "Too many requests in one time"
}
}
Body schema
Field | Data Type | Description |
---|---|---|
status | object | Contains error details |
└─status | string | Overall status, set to ERROR for non-2xx responses |
└─code | string | Code indicating the error category |
└─msg | string | Descriptive error message |
An unknown error. Please try again later.
{
"status": {
"status": "ERROR",
"code": "E_UNNOWN",
"msg": "Unknown error."
}
}
Body schema
Field | Data Type | Description |
---|---|---|
status | object | Contains error details |
└─status | string | Overall status, set to ERROR for non-2xx responses |
└─code | string | Code indicating the error category |
└─msg | string | Descriptive error message |