Discovery Order Guide¶
Let OpenProspect find companies for you based on your Ideal Customer Profile.
Overview¶
Discovery orders are the counterpart to enrichment orders. Instead of providing a company list for enrichment, you describe your ideal customer and OpenProspect finds matching companies for you.
| Enrichment Order | Discovery Order | |
|---|---|---|
| Input | Your company list | Your Ideal Customer Profile |
| Output | Enriched data for your companies | New companies matching your ICP |
order_type |
ENRICHMENT |
DISCOVERY |
companies field |
Required (1-10,000) | Not allowed |
source_id in results |
Your external_id |
null (system-discovered) |
Order Lifecycle¶
Discovery orders follow the same 5-status lifecycle as enrichment orders:
stateDiagram-v2
[*] --> RECEIVED: Create order
RECEIVED --> ACCEPTED: Admin review
RECEIVED --> CANCELLED: Cancel
ACCEPTED --> IN_PROGRESS: Discovery starts
ACCEPTED --> CANCELLED: Cancel
IN_PROGRESS --> COMPLETED: Companies found & enriched
IN_PROGRESS --> CANCELLED: Cancel
COMPLETED --> [*]
CANCELLED --> [*]
| Status | What It Means |
|---|---|
RECEIVED |
Your order is queued for admin review |
ACCEPTED |
Approved and the discovery pipeline is starting |
IN_PROGRESS |
Actively searching for and enriching companies |
COMPLETED |
All data is ready \u2014 retrieve your results |
CANCELLED |
Order was cancelled |
Prerequisites¶
Before you begin:
- API Key with
orders:read,orders:write,blacklists:read, andblacklists:writescopes. Create one in the OpenProspect dashboard under Settings > Developer > API Keys. - Base URL:
https://api.openprospect.io/api/v1
Scopes
Discovery orders need four scopes: orders:read, orders:write for order management, plus blacklists:read, blacklists:write for blacklist management. This combination is available as the Discovery partner scope set in the dashboard.
Step 1: Create a Blacklist (Optional)¶
If you want to exclude known companies from discovery results, create a blacklist first. This is optional but recommended to avoid discovering companies you already know.
See the Blacklist Management Guide for full details. Quick summary:
# Create a blacklist
curl -X POST "https://api.openprospect.io/api/v1/blacklists" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Existing Customers", "description": "Companies already in our CRM"}'
# Add entries
curl -X POST "https://api.openprospect.io/api/v1/blacklists/BLACKLIST_ID/entries" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"entries": [
{"company_name": "Acme Corp", "website_url": "https://www.acme-corp.com"},
{"company_name": "Global Industries GmbH", "city": "Munich", "country": "Germany"}
]
}'
Matching Accuracy
Always provide website_url in blacklist entries when available. Domain-based matching is more reliable than name-only matching. See Matching Accuracy for details.
Step 2: Create a Discovery Order¶
Submit your Ideal Customer Profile and let OpenProspect find matching companies.
Request¶
POST /api/v1/orders
curl -X POST "https://api.openprospect.io/api/v1/orders" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"order_type": "DISCOVERY",
"title": "DACH SaaS Discovery Q1 2026",
"features": ["COMPANY_DATA", "CONTACTS", "ANALYSIS"],
"ideal_customer_profile": "Mid-market SaaS companies in the DACH region with 50-500 employees, focused on B2B software solutions",
"seller_offering": "AI-powered lead generation and prospect enrichment platform",
"seller_industry": "Software / Technology",
"target_countries": ["Germany", "Austria", "Switzerland"],
"target_cities": ["Berlin", "Munich", "Vienna"],
"excluded_industries": ["Government", "Non-profit"],
"output_language": "en",
"blacklist_ids": ["d33b69ce-80ce-4ede-b4df-4b11427f8b55"],
"profile_name": "DACH SaaS Discovery Profile"
}'
import httpx
API_KEY = "lnc_live_your_api_key_here"
BASE_URL = "https://api.openprospect.io/api/v1"
order = {
"order_type": "DISCOVERY",
"title": "DACH SaaS Discovery Q1 2026",
"features": ["COMPANY_DATA", "CONTACTS", "ANALYSIS"],
"ideal_customer_profile": (
"Mid-market SaaS companies in the DACH region "
"with 50-500 employees, focused on B2B software solutions"
),
"seller_offering": "AI-powered lead generation and prospect enrichment platform",
"seller_industry": "Software / Technology",
"target_countries": ["Germany", "Austria", "Switzerland"],
"target_cities": ["Berlin", "Munich", "Vienna"],
"excluded_industries": ["Government", "Non-profit"],
"output_language": "en",
"blacklist_ids": ["d33b69ce-80ce-4ede-b4df-4b11427f8b55"],
"profile_name": "DACH SaaS Discovery Profile",
}
response = httpx.post(
f"{BASE_URL}/orders",
json=order,
headers={"Authorization": f"Bearer {API_KEY}"},
)
if response.status_code == 202:
data = response.json()
print(f"Discovery order created: {data['order_id']}")
print(f"Status: {data['status']}")
else:
print(f"Error {response.status_code}: {response.json()}")
const API_KEY = "lnc_live_your_api_key_here";
const BASE_URL = "https://api.openprospect.io/api/v1";
const order = {
order_type: "DISCOVERY",
title: "DACH SaaS Discovery Q1 2026",
features: ["COMPANY_DATA", "CONTACTS", "ANALYSIS"],
ideal_customer_profile:
"Mid-market SaaS companies in the DACH region with 50-500 employees, focused on B2B software solutions",
seller_offering:
"AI-powered lead generation and prospect enrichment platform",
seller_industry: "Software / Technology",
target_countries: ["Germany", "Austria", "Switzerland"],
target_cities: ["Berlin", "Munich", "Vienna"],
excluded_industries: ["Government", "Non-profit"],
output_language: "en",
blacklist_ids: ["d33b69ce-80ce-4ede-b4df-4b11427f8b55"],
profile_name: "DACH SaaS Discovery Profile",
};
const response = await fetch(`${BASE_URL}/orders`, {
method: "POST",
headers: {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(order),
});
if (response.status === 202) {
const data = await response.json();
console.log(`Discovery order created: ${data.order_id}`);
console.log(`Status: ${data.status}`);
} else {
console.error(`Error ${response.status}:`, await response.json());
}
const API_KEY = "lnc_live_your_api_key_here";
const BASE_URL = "https://api.openprospect.io/api/v1";
interface CreateOrderResponse {
order_id: string;
order_type: "ENRICHMENT" | "DISCOVERY";
status: string;
company_count: number;
estimated_cost: { total: number; currency: string } | null;
message: string;
}
const order = {
order_type: "DISCOVERY" as const,
title: "DACH SaaS Discovery Q1 2026",
features: ["COMPANY_DATA", "CONTACTS", "ANALYSIS"],
ideal_customer_profile:
"Mid-market SaaS companies in the DACH region with 50-500 employees, focused on B2B software solutions",
seller_offering:
"AI-powered lead generation and prospect enrichment platform",
seller_industry: "Software / Technology",
target_countries: ["Germany", "Austria", "Switzerland"],
target_cities: ["Berlin", "Munich", "Vienna"],
excluded_industries: ["Government", "Non-profit"],
output_language: "en",
blacklist_ids: ["d33b69ce-80ce-4ede-b4df-4b11427f8b55"],
profile_name: "DACH SaaS Discovery Profile",
};
const response = await fetch(`${BASE_URL}/orders`, {
method: "POST",
headers: {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(order),
});
if (response.status === 202) {
const data: CreateOrderResponse = await response.json();
console.log(`Discovery order created: ${data.order_id}`);
console.log(`Status: ${data.status}`);
} else {
console.error(`Error ${response.status}:`, await response.json());
}
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
var apiKey = "lnc_live_your_api_key_here";
var baseUrl = "https://api.openprospect.io/api/v1";
var order = new
{
order_type = "DISCOVERY",
title = "DACH SaaS Discovery Q1 2026",
features = new[] { "COMPANY_DATA", "CONTACTS", "ANALYSIS" },
ideal_customer_profile =
"Mid-market SaaS companies in the DACH region with 50-500 employees, focused on B2B software solutions",
seller_offering =
"AI-powered lead generation and prospect enrichment platform",
seller_industry = "Software / Technology",
target_countries = new[] { "Germany", "Austria", "Switzerland" },
target_cities = new[] { "Berlin", "Munich", "Vienna" },
excluded_industries = new[] { "Government", "Non-profit" },
output_language = "en",
blacklist_ids = new[] { "d33b69ce-80ce-4ede-b4df-4b11427f8b55" },
profile_name = "DACH SaaS Discovery Profile"
};
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", apiKey);
var json = JsonSerializer.Serialize(order);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync($"{baseUrl}/orders", content);
if ((int)response.StatusCode == 202)
{
var body = await response.Content.ReadAsStringAsync();
Console.WriteLine($"Discovery order created: {body}");
}
else
{
Console.WriteLine($"Error {(int)response.StatusCode}");
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
Response (202 Accepted)¶
{
"order_id": "74f00d79-7e4a-4ba3-a30c-012de85d6d78",
"order_type": "DISCOVERY",
"status": "RECEIVED",
"company_count": 0,
"estimated_cost": null,
"message": "Order received. Awaiting admin review."
}
Checkpoint
Save the order_id from the response. You need it for all subsequent calls.
Discovery Response Differences
company_countis0at submission (companies haven't been discovered yet)estimated_costisnull(cost is determined after discovery)
Request Parameters¶
Required Fields¶
| Parameter | Type | Max Length | Description |
|---|---|---|---|
order_type |
string | -- | Must be "DISCOVERY" |
title |
string | 500 | A descriptive name for your order |
features |
string[] | -- | Enrichment features to apply to discovered companies (see Features Reference) |
ideal_customer_profile |
string | 5,000 | Detailed description of your ideal customer |
seller_offering |
string | 5,000 | What your company sells \u2014 used for match scoring |
target_countries |
string[] | -- | Countries to search in (e.g., ["Germany", "Austria"]) |
Optional Fields¶
| Parameter | Type | Max Length | Description |
|---|---|---|---|
seller_industry |
string | 1,000 | Your company's industry |
target_cities |
string[] | -- | Specific cities to focus on |
excluded_industries |
string[] | -- | Industries to exclude from results |
output_language |
string | 10 | Language code for output (e.g., "en", "de") |
blacklist_ids |
UUID[] | -- | Blacklist IDs to assign. Excluded companies won't appear in results. |
priority |
string | -- | "NORMAL" (default) or "EXPRESS" |
webhook_url |
string | -- | URL for status change notifications |
contact_roles |
string[] | -- | Target contact roles (e.g., ["CEO", "CTO"]) |
profile_name |
string | 500 | Name for auto-created profile. Mutually exclusive with profile_id. |
profile_id |
UUID | -- | Existing profile ID. Mutually exclusive with profile_name. |
Forbidden Fields
Discovery orders cannot include a companies array. If you want to enrich specific companies, use an enrichment order instead.
Writing an Effective ICP¶
The ideal_customer_profile field drives the quality of discovery results. Be specific:
Good ICP:
"Mid-market B2B SaaS companies in the DACH region with 50-500 employees, focused on enterprise software solutions for HR, finance, or operations. Ideally companies that have raised Series A-C funding and are actively growing their sales team."
Too vague:
"Tech companies in Europe"
Include details about:
- Industry / vertical (e.g., "B2B SaaS", "hospitality", "logistics")
- Company size (employee count, revenue range)
- Geography (use
target_countriesandtarget_citiesfor precision) - Business characteristics (growth stage, tech stack, hiring signals)
- What makes them a good fit for your product/service
Step 3: Track Order Status¶
After creating a discovery order, poll the status endpoint to know when results are ready.
Request¶
GET /api/v1/orders/{order_id}
import time
order_id = "74f00d79-7e4a-4ba3-a30c-012de85d6d78"
while True:
response = httpx.get(
f"{BASE_URL}/orders/{order_id}",
headers={"Authorization": f"Bearer {API_KEY}"},
)
if response.status_code != 200:
print(f"Polling error {response.status_code}, retrying...")
time.sleep(60)
continue
data = response.json()
status = data["status"]
print(f"Order status: {status}")
if status == "COMPLETED":
print(f"Results are ready! {data['company_count']} companies found.")
break
elif status == "CANCELLED":
print("Order was cancelled.")
break
time.sleep(60) # Check every minute
const orderId = "74f00d79-7e4a-4ba3-a30c-012de85d6d78";
async function pollOrderStatus() {
while (true) {
const response = await fetch(`${BASE_URL}/orders/${orderId}`, {
headers: { Authorization: `Bearer ${API_KEY}` },
});
if (!response.ok) {
console.warn(`Polling error ${response.status}, retrying...`);
await new Promise((resolve) => setTimeout(resolve, 60000));
continue;
}
const data = await response.json();
console.log(`Order status: ${data.status}`);
if (data.status === "COMPLETED") {
console.log(`Results are ready! ${data.company_count} companies found.`);
return data;
}
if (data.status === "CANCELLED") {
console.log("Order was cancelled.");
return data;
}
await new Promise((resolve) => setTimeout(resolve, 60000));
}
}
await pollOrderStatus();
interface OrderStatusResponse {
order_id: string;
order_type: "ENRICHMENT" | "DISCOVERY";
title: string;
status: "RECEIVED" | "ACCEPTED" | "IN_PROGRESS" | "COMPLETED" | "CANCELLED";
priority: string;
company_count: number;
features: string[];
estimated_cost: { total: number; currency: string } | null;
prospect_search_id: string | null;
admin_notes: string | null;
webhook_url: string | null;
seller_offering: string | null;
seller_industry: string | null;
excluded_industries: string[] | null;
target_countries: string[] | null;
target_cities: string[] | null;
output_language: string | null;
created_at: string;
accepted_at: string | null;
completed_at: string | null;
cancelled_at: string | null;
}
const orderId = "74f00d79-7e4a-4ba3-a30c-012de85d6d78";
async function pollOrderStatus(): Promise<OrderStatusResponse> {
while (true) {
const response = await fetch(`${BASE_URL}/orders/${orderId}`, {
headers: { Authorization: `Bearer ${API_KEY}` },
});
if (!response.ok) {
console.warn(`Polling error ${response.status}, retrying...`);
await new Promise((resolve) => setTimeout(resolve, 60000));
continue;
}
const data: OrderStatusResponse = await response.json();
console.log(`Order status: ${data.status}`);
if (data.status === "COMPLETED" || data.status === "CANCELLED") {
return data;
}
await new Promise((resolve) => setTimeout(resolve, 60000));
}
}
const result = await pollOrderStatus();
var orderId = "74f00d79-7e4a-4ba3-a30c-012de85d6d78";
while (true)
{
var response = await client.GetAsync($"{baseUrl}/orders/{orderId}");
if (!response.IsSuccessStatusCode)
{
Console.WriteLine($"Polling error {(int)response.StatusCode}, retrying...");
await Task.Delay(60000);
continue;
}
var body = await response.Content.ReadAsStringAsync();
var data = JsonSerializer.Deserialize<JsonElement>(body);
var status = data.GetProperty("status").GetString();
Console.WriteLine($"Order status: {status}");
if (status == "COMPLETED")
{
var count = data.GetProperty("company_count").GetInt32();
Console.WriteLine($"Results are ready! {count} companies found.");
break;
}
if (status == "CANCELLED")
{
Console.WriteLine("Order was cancelled.");
break;
}
await Task.Delay(60000); // Check every minute
}
Response (200 OK)¶
{
"order_id": "74f00d79-7e4a-4ba3-a30c-012de85d6d78",
"order_type": "DISCOVERY",
"title": "DACH SaaS Discovery Q1 2026",
"status": "RECEIVED",
"priority": "NORMAL",
"company_count": 0,
"features": ["COMPANY_DATA", "CONTACTS", "ANALYSIS"],
"estimated_cost": null,
"prospect_search_id": null,
"admin_notes": null,
"webhook_url": null,
"seller_offering": "AI-powered lead generation and prospect enrichment platform",
"seller_industry": "Software / Technology",
"excluded_industries": ["Government", "Non-profit"],
"target_countries": ["Germany", "Austria", "Switzerland"],
"target_cities": ["Berlin", "Munich", "Vienna"],
"output_language": "en",
"created_at": "2026-03-01T10:30:00.000000Z",
"accepted_at": null,
"completed_at": null,
"cancelled_at": null
}
The status response includes all discovery-specific fields (seller_offering, seller_industry, excluded_industries, target_countries, target_cities, output_language) so you can verify your order configuration.
List All Orders¶
You can also list all your orders with optional status filtering:
curl -X GET "https://api.openprospect.io/api/v1/orders?status=COMPLETED&limit=10" \
-H "Authorization: Bearer YOUR_API_KEY"
Discovery orders appear alongside enrichment orders, distinguished by order_type: "DISCOVERY".
Step 4: Retrieve Results¶
Once the order is COMPLETED, retrieve the discovered companies with all their enriched data.
Request¶
GET /api/v1/orders/{order_id}/results
order_id = "74f00d79-7e4a-4ba3-a30c-012de85d6d78"
all_companies = []
offset = 0
limit = 50
while True:
response = httpx.get(
f"{BASE_URL}/orders/{order_id}/results",
params={"limit": limit, "offset": offset},
headers={"Authorization": f"Bearer {API_KEY}"},
)
data = response.json()
for company in data["items"]:
print(f"Company: {company['name']}")
print(f" Match Score: {company['match_score']}")
print(f" Prospects: {len(company['prospects'])}")
all_companies.append(company)
if not data["has_more"]:
break
offset += limit
print(f"\nTotal companies discovered: {len(all_companies)}")
const orderId = "74f00d79-7e4a-4ba3-a30c-012de85d6d78";
const allCompanies = [];
let offset = 0;
const limit = 50;
while (true) {
const response = await fetch(
`${BASE_URL}/orders/${orderId}/results?limit=${limit}&offset=${offset}`,
{ headers: { Authorization: `Bearer ${API_KEY}` } }
);
const data = await response.json();
for (const company of data.items) {
console.log(`Company: ${company.name}`);
console.log(` Match Score: ${company.match_score}`);
console.log(` Prospects: ${company.prospects.length}`);
allCompanies.push(company);
}
if (!data.has_more) break;
offset += limit;
}
console.log(`\nTotal companies discovered: ${allCompanies.length}`);
interface Prospect {
id: string;
company_id: string;
first_name: string;
last_name: string | null;
job_title: string | null;
email: string | null;
phone_number: string | null;
linkedin_url: string | null;
qualification_score: number | null;
qualification_reason: string | null;
email_type: string | null;
validation_confidence: number | null;
bounce_risk_score: number | null;
}
interface Company {
id: string;
name: string;
website_url: string | null;
normalized_url: string | null;
business_type: string | null;
city: string | null;
country: string | null;
description: string | null;
employee_count: number | null;
phone_numbers: string[] | null;
social_media_links: { platform: string; url: string }[] | null;
match_score: number | null;
match_reasons: string[] | null;
match_reasoning: string | null;
web_technologies: string[] | null;
web_technologies_analyzed_at: string | null;
source_id: string | null;
delivered_at: string | null;
legal_registration_info: Record<string, unknown> | null;
prospects: Prospect[];
}
interface ResultsResponse {
items: Company[];
total: number;
limit: number;
offset: number;
has_more: boolean;
}
const orderId = "74f00d79-7e4a-4ba3-a30c-012de85d6d78";
const allCompanies: Company[] = [];
let offset = 0;
const limit = 50;
while (true) {
const response = await fetch(
`${BASE_URL}/orders/${orderId}/results?limit=${limit}&offset=${offset}`,
{ headers: { Authorization: `Bearer ${API_KEY}` } }
);
const data: ResultsResponse = await response.json();
allCompanies.push(...data.items);
if (!data.has_more) break;
offset += limit;
}
console.log(`Total companies discovered: ${allCompanies.length}`);
var orderId = "74f00d79-7e4a-4ba3-a30c-012de85d6d78";
var offset = 0;
var limit = 50;
var allCompanies = new List<JsonElement>();
while (true)
{
var response = await client.GetAsync(
$"{baseUrl}/orders/{orderId}/results?limit={limit}&offset={offset}");
var body = await response.Content.ReadAsStringAsync();
var data = JsonSerializer.Deserialize<JsonElement>(body);
foreach (var company in data.GetProperty("items").EnumerateArray())
{
Console.WriteLine($"Company: {company.GetProperty("name")}");
Console.WriteLine($" Match Score: {company.GetProperty("match_score")}");
allCompanies.Add(company);
}
if (!data.GetProperty("has_more").GetBoolean()) break;
offset += limit;
}
Console.WriteLine($"\nTotal companies discovered: {allCompanies.Count}");
Response (200 OK)¶
{
"items": [
{
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"name": "CloudOps Technologies GmbH",
"website_url": "https://www.cloudops-tech.de",
"normalized_url": "cloudops-tech.de",
"business_type": "B2B SaaS",
"city": "Berlin",
"country": "Germany",
"description": "Cloud operations platform for enterprise DevOps teams...",
"employee_count": 85,
"phone_numbers": ["+49 30 98765432"],
"social_media_links": [
{"platform": "linkedin", "url": "https://linkedin.com/company/cloudops-tech"}
],
"match_score": 9,
"match_reasons": ["Industry match", "Size match", "Location match", "Growth signals"],
"match_reasoning": "Strong ICP fit: B2B SaaS company in Berlin with 85 employees...",
"web_technologies": ["React", "AWS", "Kubernetes"],
"web_technologies_analyzed_at": "2026-03-02T14:00:00.000000Z",
"source_id": null,
"delivered_at": "2026-03-02T14:30:00.000000Z",
"legal_registration_info": null,
"prospects": [
{
"id": "f6a7b8c9-d0e1-2345-f6a7-b8c9d0e12345",
"company_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"first_name": "Thomas",
"last_name": "Weber",
"job_title": "CTO",
"email": "t.weber@cloudops-tech.de",
"phone_number": null,
"linkedin_url": "https://linkedin.com/in/thomasweber",
"qualification_score": 9,
"qualification_reason": "C-level technology decision maker",
"email_type": "PERSONAL",
"validation_confidence": 92,
"bounce_risk_score": 8
}
]
}
],
"total": 47,
"limit": 50,
"offset": 0,
"has_more": false
}
Discovery Results vs Enrichment Results
Discovery results have the same structure as enrichment results, with one key difference: source_id is null for discovered companies because they were found by the system, not submitted by you. Use the company id field for your own tracking.
Pagination¶
| Parameter | Default | Range | Description |
|---|---|---|---|
limit |
50 | 1-100 | Companies per page |
offset |
0 | 0+ | Number of companies to skip |
delivered_since |
-- | ISO 8601 | Only companies delivered after this timestamp |
Webhook Notifications¶
Instead of polling, you can receive status change notifications via webhook. Include webhook_url when creating your order:
{
"order_type": "DISCOVERY",
"title": "DACH SaaS Discovery Q1 2026",
"features": ["COMPANY_DATA", "CONTACTS", "ANALYSIS"],
"ideal_customer_profile": "...",
"seller_offering": "...",
"target_countries": ["Germany"],
"webhook_url": "https://your-api.com/webhooks/openprospect"
}
When the order status changes, we send a POST request to your URL:
{
"event": "order.status_changed",
"order_id": "74f00d79-7e4a-4ba3-a30c-012de85d6d78",
"status": "COMPLETED",
"previous_status": "IN_PROGRESS",
"timestamp": "2026-03-02T14:30:00.000000+00:00"
}
For webhook security options and retry policy, see the Enrichment Order Guide - Webhooks.
Error Handling¶
Discovery-Specific Validation Errors (422)¶
| Error | Cause | Fix |
|---|---|---|
ideal_customer_profile is required |
Missing ICP for discovery | Provide a detailed ICP description |
seller_offering is required |
Missing seller offering | Describe what your company sells |
target_countries is required |
Missing target countries | Provide at least one target country |
companies is not allowed |
Included companies array | Remove companies \u2014 discovery finds companies for you |
| Duplicate features | Same feature listed twice | Remove duplicates from features |
| Profile conflict | Both profile_id and profile_name |
Use one or the other |
Other Errors¶
| Status | Code | Cause |
|---|---|---|
| 401 | UNAUTHORIZED |
Invalid or expired API key |
| 403 | AUTHORIZATION_ERROR |
Missing required scopes |
| 404 | ORDER_NOT_FOUND |
Order ID does not exist |
For the full error reference, see Error Codes.
FAQ¶
How long does discovery take?¶
Discovery orders typically take longer than enrichment orders because the system needs to find companies before enriching them. Processing time depends on the breadth of your ICP and target geography.
Orders are processed by our admin team. You receive a webhook notification (or can poll) when processing completes.
How many companies will be discovered?¶
The number of results depends on your ICP specificity, target geography, and the availability of matching companies. More specific ICPs tend to produce fewer but higher-quality matches.
Can I combine discovery with enrichment features?¶
Yes. The features array works the same way for both order types. Common discovery combinations:
- Minimum:
["COMPANY_DATA"]\u2014 basic company profiles - Standard:
["COMPANY_DATA", "CONTACTS", "ANALYSIS"]\u2014 profiles + contacts + match scoring - Full:
["COMPANY_DATA", "CONTACTS", "ANALYSIS", "TECHNOLOGIES", "EMAIL_DISCOVERY"]\u2014 everything
See Enrichment Features for the complete list.
What does source_id: null mean in results?¶
For enrichment orders, source_id maps to the external_id you submitted with each company. For discovery orders, source_id is always null because the companies were found by the system, not submitted by you.
Can I attach blacklists after creating an order?¶
No. Blacklists must be specified at order creation via blacklist_ids. To change blacklists, create a new order.
How do discovery orders work with the enrichment guide?¶
Discovery and enrichment orders share the same endpoints (POST /api/v1/orders, GET /api/v1/orders/{id}, GET /api/v1/orders/{id}/results). The key differences are in the request body (ICP fields vs company list) and response shapes. See the comparison table at the top of this guide.
API Reference Summary¶
| Method | Path | Scope | Description |
|---|---|---|---|
POST |
/api/v1/orders |
orders:write |
Create discovery order (202) with order_type: "DISCOVERY" |
GET |
/api/v1/orders |
orders:read |
List your orders |
GET |
/api/v1/orders/{order_id} |
orders:read |
Get order status |
GET |
/api/v1/orders/{order_id}/results |
orders:read |
Get discovered companies |
See also: Blacklist Management Guide for managing company exclusion lists.