Search leads
Search for people who match one or more Lead Finder criteria. This endpoint returns a compact result set intended for discovery, filtering, and selecting leads for further enrichment.
Use the returned lead uid and other lead details when queueing a lead for enrichment with the queue lead enrichments endpoint.
Request
Each request must include a non-empty search_criteria array. Every criterion contains a name, a value, and an operator, where INCLUDE narrows the search to matching values and EXCLUDE removes matching values from the result set.
To match multiple values for the same criterion, add multiple search_criteria objects with the same name. For enumerated criteria such as COUNTRY or INDUSTRY, fetch current allowed values from search criteria values before building the request.
This call can use account credits. Searching for leads and viewing returned lead data costs 1 credit per lead.
Endpoint
POST https://api.woodpecker.co/rest/v2/lead_finder/leads
Headers
x-api-key: {YOUR_API_KEY}
Content-Type: application/json
For details on how to authenticate your requests, please see the authentication guide.
Body
{
"search_criteria": [
{
"name": "INDUSTRY",
"value": "banking",
"operator": "INCLUDE"
},
{
"name": "CURRENT_JOB_TITLE",
"value": "ceo",
"operator": "INCLUDE"
},
{
"name": "COUNTRY",
"value": "poland",
"operator": "EXCLUDE"
}
],
"size": 5
}
Body schema
| Field | Type | Required | Description |
|---|---|---|---|
search_criteria | array[object] | Yes | Non-empty list of filters to apply |
└─ name | string | Yes | Criterion name from the search criteria catalog. Case-sensitive. |
└─ value | string | Yes | Criterion value. For enumerated criteria (case-sensitive), fetch allowed values from search criteria values. |
└─ operator | string | Yes | Filter behavior: INCLUDE keeps matches, EXCLUDE removes matches |
size | integer | No | Number of leads to return. Default: 1. Maximum: 50 |
next_page | string | No | Pagination token returned by a previous search response |
Request samples
Search for leads with inclusion and exclusion filters
- cURL
- Python
- Java
- Node.js
- PHP
curl --request POST \
--url "https://api.woodpecker.co/rest/v2/lead_finder/leads" \
--header "x-api-key: {YOUR_API_KEY}" \
--header "Content-Type: application/json" \
--data '{
"search_criteria": [
{
"name": "INDUSTRY",
"value": "banking",
"operator": "INCLUDE"
},
{
"name": "COUNTRY",
"value": "poland",
"operator": "EXCLUDE"
},
{
"name": "CURRENT_JOB_TITLE",
"value": "ceo",
"operator": "INCLUDE"
}
],
"size": 2
}'
import requests
def search_leads():
url = "https://api.woodpecker.co/rest/v2/lead_finder/leads"
headers = {
"x-api-key": "{YOUR_API_KEY}",
"Content-Type": "application/json"
}
payload = {
"search_criteria": [
{
"name": "INDUSTRY",
"value": "banking",
"operator": "INCLUDE"
},
{
"name": "COUNTRY",
"value": "poland",
"operator": "EXCLUDE"
},
{
"name": "CURRENT_JOB_TITLE",
"value": "ceo",
"operator": "INCLUDE"
}
],
"size": 2
}
response = requests.post(url, headers=headers, json=payload)
if response.status_code == 200:
print("POST response:", response.json())
else:
print("POST failed with status:", response.status_code, response.text)
if __name__ == "__main__":
search_leads()
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class WoodpeckerApiClient {
private static final String API_KEY = "{YOUR_API_KEY}";
public static void main(String[] args) {
searchLeads();
}
public static void searchLeads() {
try {
String url = "https://api.woodpecker.co/rest/v2/lead_finder/leads";
String jsonData = """
{
"search_criteria": [
{
"name": "INDUSTRY",
"value": "banking",
"operator": "INCLUDE"
},
{
"name": "COUNTRY",
"value": "poland",
"operator": "EXCLUDE"
},
{
"name": "CURRENT_JOB_TITLE",
"value": "ceo",
"operator": "INCLUDE"
}
],
"size": 2
}
""";
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI(url))
.header("x-api-key", API_KEY)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(jsonData))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
System.out.println("POST response: " + response.body());
} else {
System.err.println("POST request failed: " + response.statusCode());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
const axios = require("axios");
async function searchLeads() {
const url = "https://api.woodpecker.co/rest/v2/lead_finder/leads";
const headers = {
"x-api-key": "{YOUR_API_KEY}",
"Content-Type": "application/json"
};
const data = {
search_criteria: [
{
name: "INDUSTRY",
value: "banking",
operator: "INCLUDE"
},
{
name: "COUNTRY",
value: "poland",
operator: "EXCLUDE"
},
{
name: "CURRENT_JOB_TITLE",
value: "ceo",
operator: "INCLUDE"
}
],
size: 2
};
try {
const response = await axios.post(url, data, { headers });
if (response.status === 200) {
console.log("POST response:", response.data);
} else {
console.error("POST request failed:", response.status);
}
} catch (error) {
console.error("Request error:", error.response?.status || error.message);
}
}
searchLeads();
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
$client = new Client([
'base_uri' => 'https://api.woodpecker.co/rest/v2/',
'headers' => [
'x-api-key' => getenv('WOODPECKER_API_KEY'),
'Content-Type' => 'application/json',
],
]);
try {
$response = $client->post('lead_finder/leads', [
'json' => [
'search_criteria' => [
[
'name' => 'INDUSTRY',
'value' => 'banking',
'operator' => 'INCLUDE',
],
[
'name' => 'COUNTRY',
'value' => 'poland',
'operator' => 'EXCLUDE',
],
[
'name' => 'CURRENT_JOB_TITLE',
'value' => 'ceo',
'operator' => 'INCLUDE',
],
],
'size' => 2,
],
]);
echo $response->getStatusCode(), "\n";
echo $response->getBody(), "\n";
} catch (RequestException $e) {
echo "Error: ", $e->getMessage(), "\n";
if ($e->hasResponse()) {
echo $e->getResponse()->getBody(), "\n";
}
}
Response
Response examples
- 200
- 400
- 401
- 402
- 404
- 500
Preview of the leads matching the search criteria
{
"size": 2,
"leads": [
{
"uid": "0SVOUN5zYlkzJ5fMrBxUdiQ_0000",
"full_name": "Erlich Bachman",
"first_name": "Erlich",
"last_name": "Bachman",
"gender": "Male",
"linkedin_url": "linkedin.com/in/erlich-bachman-404xyz",
"company_name": "Bachmanity",
"company_website": "bachmanity.com",
"industry": "Software as a Service",
"job_title": "Ceo",
"job_title_role": "Operations",
"job_title_levels": [
"Cxo"
],
"location_name": "Palo Alto, California, United States",
"city": "Palo Alto",
"state": "California",
"country": "United States"
},
{
"uid": "4fl-ZIc98WqIFbOFgcRzkw_0000",
"full_name": "Jared Dunn",
"first_name": "Jared",
"last_name": "Dunn",
"gender": "Male",
"linkedin_url": "linkedin.com/in/jared-dunn-pp",
"company_name": "Pied Piper",
"company_website": "piedpiper.com",
"industry": "Software as a Service",
"job_title": "Ceo",
"job_title_role": "Operations",
"job_title_levels": [
"Cxo"
],
"location_name": "Palo Alto, California, United States",
"city": "Palo Alto",
"state": "California",
"country": "United States"
}
],
"total_found": 28639,
"next_page": "206$8.458225"
}
Body schema
| Field | Type | Description |
|---|---|---|
size | integer | Number of leads requested in this page |
leads | array[object] | Lead search result items |
└─ uid | string | Lead identifier |
└─ full_name | string | Full name of the lead |
└─ first_name | string | First name |
└─ last_name | string | Last name |
└─ gender | string | Gender |
└─ linkedin_url | string | LinkedIn profile URL without a http prefix |
└─ company_name | string | Current company name |
└─ company_website | string | Current company website |
└─ industry | string | Current industry |
└─ job_title | string | Current job title |
└─ job_title_role | string | Job title role when available |
└─ job_title_levels | array[string] | Job seniority levels |
└─ location_name | string | Full human-readable location |
└─ city | string | City |
└─ state | string | State or region |
└─ country | string | Country |
total_found | integer | Total number of matches available for the current search |
next_page | string | Pagination cursor. Use to request the next page of results |
Invalid search criteria, unsupported pagination usage, or another business validation error.
{
"title": "Bad Request",
"status": 400,
"details": "Value of next_page is incorrect: must be null.",
"timestamp": "2025-03-05 17:57:00",
"extra": null
}
Body schema
| Field | Type | Description |
|---|---|---|
title | string | A short title describing the error |
status | integer | The HTTP status code |
details | string | A detailed message explaining the validation or request problem |
timestamp | string | The timestamp when the error occurred, YYYY-MM-DD HH:MM:SS UTC |
extra | string/null | Additional information about the error, when available |
An issue with authorization. Please review the authentication guide.
{
"title": "Unauthorized",
"status": 401,
"details": "Invalid api key",
"timestamp": "2025-03-05 17:57:00"
}
Body schema
| Field | Type | Description |
|---|---|---|
title | string | A short title describing the error |
status | integer | The HTTP status code |
details | string | A detailed message explaining the authorization problem |
timestamp | string | The timestamp when the error occurred, YYYY-MM-DD HH:MM:SS UTC |
Not enough credits. An account admin can manage credits in the billing section of the app.
{
"title": "Payment Required",
"status": 402,
"details": "Insufficient credits to find leads",
"timestamp": "2025-03-05 17:57:00",
"extra": null
}
Body schema
| Field | Type | Description |
|---|---|---|
title | string | A short title describing the error |
status | integer | The HTTP status code |
details | string | A detailed message explaining the payment or credits problem |
timestamp | string | The timestamp when the error occurred, YYYY-MM-DD HH:MM:SS UTC |
extra | string/null | Additional information about the error, when available |
Please review the request URL.
{
"title": "Not Found",
"status": 404,
"details": "Requested resource does not exist",
"timestamp": "2025-03-05 17:57:00"
}
Body schema
| Field | Type | Description |
|---|---|---|
title | string | A short title describing the error |
status | integer | The HTTP status code |
details | string | A detailed message explaining the error |
timestamp | string | The timestamp when the error occurred, YYYY-MM-DD HH:MM:SS UTC |
Unexpected error, please try again later.
{
"title": "Internal server error",
"status": 500,
"details": "An unexpected error has occurred. Please try again later.",
"timestamp": "2025-03-05 17:57:00"
}
Body schema
| Field | Type | Description |
|---|---|---|
title | string | A short title describing the error |
status | integer | The HTTP status code |
details | string | A detailed message explaining the error |
timestamp | string | The timestamp when the error occurred, YYYY-MM-DD HH:MM:SS UTC |
Pagination
This endpoint uses cursor-based pagination. The response includes a next_page token when another page is available. Store the token and resend it exactly as returned in the next request body.
Request first page:
curl --request POST \
--url "https://api.woodpecker.co/rest/v2/lead_finder/leads" \
--header "x-api-key: {YOUR_API_KEY}" \
--header "Content-Type: application/json" \
--data '{
"search_criteria": [
{
"name": "INDUSTRY",
"value": "banking",
"operator": "INCLUDE"
}
],
"size": 2
}'
Example response excerpt:
{
"size": 2,
"leads": [
{
"uid": "0SVOUNZGlkzJ5fMrBxUdiQ_0000",
"full_name": "Erlich Bachman"
}
],
"total_found": 28639,
"next_page": "206$8.458299"
}
Request the next page:
curl --request POST \
--url "https://api.woodpecker.co/rest/v2/lead_finder/leads" \
--header "x-api-key: {YOUR_API_KEY}" \
--header "Content-Type: application/json" \
--data '{
"search_criteria": [
{
"name": "INDUSTRY",
"value": "banking",
"operator": "INCLUDE"
}
],
"size": 2,
"next_page": "206$8.458299"
}'