Enrichment Quick Start¶
Create an enrichment order when you already know the companies you want OpenProspect to enrich.
Time required: About 5 minutes to submit the order. OpenProspect reviews and fulfills the order asynchronously after submission.
Prerequisites¶
- An API key with
orders:write,orders:read,companies:read, andprospects:read. - The API key stored as
OPENPROSPECT_API_KEY. - A ready
profile_id. Create one with Search Profile Creation.
Step 1: Create or Reuse a Profile¶
The profile describes how OpenProspect should qualify and brief the companies. You can use the same profile for enrichment and discovery.
curl -sS -X POST "https://api.openprospect.io/api/v1/search-profile-creations" \
-H "Authorization: Bearer ${OPENPROSPECT_API_KEY}" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: profile-enrichment-quickstart-001" \
-d '{
"profile_name": "Payments software buyers",
"ideal_customer_profile": "B2B software companies that sell to finance teams",
"seller_offering": "Payment operations software",
"target_countries": ["US", "GB", "DE"],
"contact_roles": ["CFO", "Head of Finance"],
"output_language": "en"
}'
Poll the returned profile_creation_id until it returns SUCCEEDED, then save
the profile_id.
export OPENPROSPECT_PROFILE_CREATION_ID="f7a5a926-4d42-4e30-9f40-61e5da52f7aa"
curl -sS \
"https://api.openprospect.io/api/v1/search-profile-creations/${OPENPROSPECT_PROFILE_CREATION_ID}" \
-H "Authorization: Bearer ${OPENPROSPECT_API_KEY}"
Successful profile creation returns a reusable profile ID:
{
"profile_creation_id": "f7a5a926-4d42-4e30-9f40-61e5da52f7aa",
"status": "SUCCEEDED",
"profile_id": "8d6e39fd-6d73-4cc2-9612-6ffb41c84f29",
"error_code": null,
"error_message": null,
"created_at": "2026-06-30T10:00:00Z",
"started_at": "2026-06-30T10:00:01Z",
"finished_at": "2026-06-30T10:00:08Z"
}
Checkpoint: keep OPENPROSPECT_PROFILE_ID; every order references it.
Step 2: Create an Enrichment Order¶
POST /api/v1/orders
Enrichment orders require:
order_type: "ENRICHMENT"profile_idcompanieswith 1 to 10,000 company objectsfeaturesIdempotency-Key
Do not send briefing_quantity for enrichment. The API derives it from
companies.length.
The 10,000-company schema limit does not override the request-body limit.
Deployments accept at most 10 MB by default (10,485,760 bytes). Serialize the
JSON first and split field-heavy input into smaller orders before it approaches
10 MB; an oversized request returns 413.
The endpoint accepts JSON, not CSV uploads. If your source data is a CSV file, map each row to a company object using the CSV-to-JSON field mapping.
Generate one idempotency key for each logical order. Reuse that key only when retrying the identical request body. If you change the body, generate a new key.
curl -sS -X POST "https://api.openprospect.io/api/v1/orders" \
-H "Authorization: Bearer ${OPENPROSPECT_API_KEY}" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: enrichment-order-quickstart-001" \
-d '{
"order_type": "ENRICHMENT",
"title": "My first enrichment order",
"profile_id": "'"${OPENPROSPECT_PROFILE_ID}"'",
"features": ["COMPANY_DATA", "CONTACTS"],
"companies": [
{
"company_name": "Stripe",
"external_id": "crm-001",
"website_url": "https://stripe.com"
}
]
}'
import os
import httpx
api_key = os.environ["OPENPROSPECT_API_KEY"]
profile_id = os.environ["OPENPROSPECT_PROFILE_ID"]
response = httpx.post(
"https://api.openprospect.io/api/v1/orders",
headers={
"Authorization": f"Bearer {api_key}",
"Idempotency-Key": "enrichment-order-quickstart-001",
},
json={
"order_type": "ENRICHMENT",
"title": "My first enrichment order",
"profile_id": profile_id,
"features": ["COMPANY_DATA", "CONTACTS"],
"companies": [
{
"company_name": "Stripe",
"external_id": "crm-001",
"website_url": "https://stripe.com",
}
],
},
timeout=30.0,
)
response.raise_for_status()
print(response.json())
const response = await fetch("https://api.openprospect.io/api/v1/orders", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.OPENPROSPECT_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": "enrichment-order-quickstart-001",
},
body: JSON.stringify({
order_type: "ENRICHMENT",
title: "My first enrichment order",
profile_id: process.env.OPENPROSPECT_PROFILE_ID,
features: ["COMPANY_DATA", "CONTACTS"],
companies: [
{
company_name: "Stripe",
external_id: "crm-001",
website_url: "https://stripe.com",
},
],
}),
});
if (!response.ok) {
throw new Error(`Enrichment order failed: ${response.status} ${await response.text()}`);
}
console.log(await response.json());
interface CreateOrderResponse {
order_id: string;
order_type: "DISCOVERY" | "ENRICHMENT";
status: "RECEIVED";
briefing_quantity: number;
message: string;
}
const response = await fetch("https://api.openprospect.io/api/v1/orders", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.OPENPROSPECT_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": "enrichment-order-quickstart-001",
},
body: JSON.stringify({
order_type: "ENRICHMENT",
title: "My first enrichment order",
profile_id: process.env.OPENPROSPECT_PROFILE_ID,
features: ["COMPANY_DATA", "CONTACTS"],
companies: [{ company_name: "Stripe", external_id: "crm-001", website_url: "https://stripe.com" }],
}),
});
if (!response.ok) {
throw new Error(`Enrichment order failed: ${response.status} ${await response.text()}`);
}
const order = (await response.json()) as CreateOrderResponse;
console.log(order.order_id);
using System.Net.Http.Headers;
using System.Net.Http.Json;
var apiKey = Environment.GetEnvironmentVariable("OPENPROSPECT_API_KEY")
?? throw new InvalidOperationException("OPENPROSPECT_API_KEY is not set.");
var profileId = Environment.GetEnvironmentVariable("OPENPROSPECT_PROFILE_ID")
?? throw new InvalidOperationException("OPENPROSPECT_PROFILE_ID is not set.");
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
client.DefaultRequestHeaders.Add("Idempotency-Key", "enrichment-order-quickstart-001");
var payload = new
{
order_type = "ENRICHMENT",
title = "My first enrichment order",
profile_id = profileId,
features = new[] { "COMPANY_DATA", "CONTACTS" },
companies = new[]
{
new
{
company_name = "Stripe",
external_id = "crm-001",
website_url = "https://stripe.com"
}
}
};
var response = await client.PostAsJsonAsync("https://api.openprospect.io/api/v1/orders", payload);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());
Expected response:
{
"order_id": "4f767705-03a2-4e91-a3e8-1ec3f9dea865",
"order_type": "ENRICHMENT",
"status": "RECEIVED",
"briefing_quantity": 1,
"message": "Order received. Awaiting admin review."
}
Save the order_id:
Checkpoint: keep the order_id; use it to poll status and fetch results.
Step 3: Check Order Status¶
curl -sS "https://api.openprospect.io/api/v1/orders/${ORDER_ID}" \
-H "Authorization: Bearer ${OPENPROSPECT_API_KEY}"
Poll while the status is RECEIVED, ACCEPTED, or IN_PROGRESS. OpenProspect
performs review and managed fulfillment during these active states. Stop polling
on REJECTED, FAILED, or CANCELLED; those statuses do not have completed
results.
Continue to result retrieval only after COMPLETED with
results_published: true. Results are paginated. The
default page size is 50 companies and the maximum is 100. Each example below
requests 100, advances offset by the number of returned items, and stops when
has_more is false.
This example uses jq to read the pagination metadata.
offset=0
while true; do
if ! page="$(
curl --fail-with-body -sS \
"https://api.openprospect.io/api/v1/orders/${ORDER_ID}/results?limit=100&offset=${offset}" \
-H "Authorization: Bearer ${OPENPROSPECT_API_KEY}"
)"; then
printf 'Order-results request failed; pagination stopped.\n' >&2
exit 1
fi
printf '%s\n' "${page}"
has_more="$(jq -r '.has_more' <<<"${page}")"
item_count="$(jq '.items | length' <<<"${page}")"
if [[ "${has_more}" == "false" ]]; then
break
fi
offset=$((offset + item_count))
done
import os
import httpx
api_key = os.environ["OPENPROSPECT_API_KEY"]
order_id = os.environ["ORDER_ID"]
companies = []
offset = 0
while True:
response = httpx.get(
f"https://api.openprospect.io/api/v1/orders/{order_id}/results",
headers={"Authorization": f"Bearer {api_key}"},
params={"limit": 100, "offset": offset},
timeout=30.0,
)
response.raise_for_status()
page = response.json()
companies.extend(item["company"] for item in page["items"] if item["company"] is not None)
if not page["has_more"]:
break
offset += len(page["items"])
print(f"Retrieved {len(companies)} companies")
const apiKey = process.env.OPENPROSPECT_API_KEY;
const orderId = process.env.ORDER_ID;
if (!apiKey || !orderId) {
throw new Error("OPENPROSPECT_API_KEY and ORDER_ID must be set");
}
const companies = [];
let offset = 0;
while (true) {
const url = new URL(
`https://api.openprospect.io/api/v1/orders/${orderId}/results`,
);
url.searchParams.set("limit", "100");
url.searchParams.set("offset", String(offset));
const response = await fetch(url, {
headers: { Authorization: `Bearer ${apiKey}` },
});
if (!response.ok) {
throw new Error(`Order results failed: ${response.status} ${await response.text()}`);
}
const page = await response.json();
companies.push(...page.items);
if (!page.has_more) {
break;
}
offset += page.items.length;
}
console.log(`Retrieved ${companies.length} companies`);
interface DeliveredProspect {
id: string;
company_id: string;
first_name: string;
email: string | null;
email_status: string | null;
email_type: string | null;
email_delivery_quality: string | null;
email_delivery_quality_reason: string | null;
email_client_deliverable: boolean;
}
interface DeliveredCompany {
id: string;
name: string;
source_id: string | null;
prospects: DeliveredProspect[];
}
interface OrderResultsPage {
items: DeliveredCompany[];
total: number;
limit: number;
offset: number;
has_more: boolean;
}
const apiKey = process.env.OPENPROSPECT_API_KEY;
const orderId = process.env.ORDER_ID;
if (!apiKey || !orderId) {
throw new Error("OPENPROSPECT_API_KEY and ORDER_ID must be set");
}
const companies: DeliveredCompany[] = [];
let offset = 0;
while (true) {
const url = new URL(
`https://api.openprospect.io/api/v1/orders/${orderId}/results`,
);
url.searchParams.set("limit", "100");
url.searchParams.set("offset", String(offset));
const response = await fetch(url, {
headers: { Authorization: `Bearer ${apiKey}` },
});
if (!response.ok) {
throw new Error(`Order results failed: ${response.status} ${await response.text()}`);
}
const page = (await response.json()) as OrderResultsPage;
companies.push(...page.items);
if (!page.has_more) {
break;
}
offset += page.items.length;
}
console.log(`Retrieved ${companies.length} companies`);
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.Json;
var apiKey = Environment.GetEnvironmentVariable("OPENPROSPECT_API_KEY")
?? throw new InvalidOperationException("OPENPROSPECT_API_KEY is not set.");
var orderId = Environment.GetEnvironmentVariable("ORDER_ID")
?? throw new InvalidOperationException("ORDER_ID is not set.");
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
var companies = new List<JsonElement>();
var offset = 0;
while (true)
{
var url =
$"https://api.openprospect.io/api/v1/orders/{orderId}/results"
+ $"?limit=100&offset={offset}";
var response = await client.GetAsync(url);
response.EnsureSuccessStatusCode();
using var page = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
var items = page.RootElement.GetProperty("items").EnumerateArray().ToArray();
companies.AddRange(items.Select(item => item.Clone()));
if (!page.RootElement.GetProperty("has_more").GetBoolean())
{
break;
}
offset += items.Length;
}
Console.WriteLine($"Retrieved {companies.Count} companies");
For a 199-company order, limit=100 produces two result pages. A representative
first page includes pagination metadata and the public email-quality fields
email_status, email_type, email_delivery_quality,
email_delivery_quality_reason, and email_client_deliverable:
{
"items": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Example Manufacturing GmbH",
"source_id": "crm-1001",
"prospects": [
{
"id": "e5f6a7b8-c9d0-1234-e5f6-a7b8c9d01234",
"company_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"first_name": "Maria",
"email": "maria@example-manufacturing.de",
"email_status": "VERIFIED",
"email_type": "PERSONAL",
"email_delivery_quality": "client_deliverable",
"email_delivery_quality_reason": "verified_personal_email",
"email_client_deliverable": true
}
]
}
],
"total": 199,
"limit": 100,
"offset": 0,
"has_more": true
}
Each item is one submitted row: external_id echoes what you sent, status
and reason_codes explain the outcome, and company holds the enriched
company (or null for an unresolved identity). The company-level source_id
also matches your submitted external_id. If your integration accepts only
verified emails, keep prospects whose email_status == "VERIFIED". Use
email_client_deliverable == true when you want OpenProspect's stricter
default-campaign policy decision.
Every submitted row is returned. The search profile's delivery policy
annotates rows rather than dropping them: 498 submitted companies produce 498
items, of which rows that do not meet the configured contactability or quality
requirements carry status: "PARTIAL". Filter on status == "ENRICHED" for
policy-eligible companies only; available_briefing_quantity equals the
submitted count.
Do not use the profile-wide delivery endpoint to reconcile this order. It is
legacy CRM sync / drip and can contain companies from other orders for the
same profile. The immutable order result pages
(GET /api/v1/orders/{order_id}/results) are authoritative.
Checkpoint: stop polling when the order is COMPLETED, REJECTED, FAILED,
or CANCELLED. After COMPLETED with results_published: true, retrieve
pages until has_more is false. If a historical order is COMPLETED with
results_published: false, contact support with the order ID and do not fall
back to a profile-wide endpoint.
Next Steps¶
- Review all request fields and CSV mapping in the Enrichment Order Guide.
- Integrate scheduled result synchronization with Delivery Integration.
- Diagnose authentication, validation, idempotency, and lifecycle failures with Error Handling.
- Inspect exact request and response schemas in the API Reference.