Skip to content

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, and prospects: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"
}
export OPENPROSPECT_PROFILE_ID="8d6e39fd-6d73-4cc2-9612-6ffb41c84f29"

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_id
  • companies with 1 to 10,000 company objects
  • features
  • Idempotency-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:

export ORDER_ID="4f767705-03a2-4e91-a3e8-1ec3f9dea865"

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