API reference

Build with prospiq

The prospiq API is a REST interface for enriching contacts, finding work emails and direct dials, and pushing results into your stack. JSON requests, JSON responses, bearer-token authentication. You pay only for verified results.

Get an API key

Create keys in your account settings. Growth plan supports up to 2 keys, Pro up to 5. Each key can be a live key (real enrichments) or a sandbox key (mock data, no credits used).

Open settings →

Base URL

All endpoints are served from a single base URL.

https://prospiq.net/api/v1

Conventions

  • All requests must use HTTPS. Plain HTTP is rejected.
  • Request bodies are JSON with Content-Type: application/json.
  • Responses are JSON with one of the documented status codes.
  • Timestamps are ISO 8601 in UTC (for example 2026-05-19T14:30:00.000Z).
  • The current API version is v1. Breaking changes ship as v2.

Authentication

Every request must include an Authorization header with a bearer token containing your API key. Keys start with piq_live_ for production or piq_test_ for sandbox.

Authorization header
curl https://prospiq.net/api/v1/credits \
  -H "Authorization: Bearer piq_live_xxxxxxxxxxxxxxxxxxxxxxxx"
Keep keys secret

Keys carry the same authority as your account. Never commit them to source control, log them, or paste them in shared messages. We store only a one-way hash; if you lose a key, generate a new one.

Plan requirement

API access is included on Growth and Pro plans. Requests authenticated with a key whose owner is on Free or Starter return 403 Forbidden.

IP allowlisting

Each key supports an optional IP allowlist. When set, requests from any other IP return 403 Forbidden. Configure allowlists per key in account settings.

Pricing

You are charged in credits only for results we actually find. Failed lookups cost zero.

Verified email
1 credit
Verified phone
10 credits
Email + phone (type=both)
11 credits
Empty result (NOT_FOUND)
0 credits
Sandbox requests
0 credits

When credits run out, requests return 402 Payment Required. Top up from your plan page.

Rate limits

Limits are enforced per account, not per key. Sandbox requests count toward usage but are not metered against the daily cap.

Growth
Daily limit500 req/day
Per minute100 req/min
Max keys2 active
Bulk per request50 contacts
Pro
Daily limit1,000 req/day
Per minute200 req/min
Max keys5 active
Bulk per request100 contacts

Exceeded limits return 429 Too Many Requests. Counters reset daily at midnight UTC.

Errors

The API uses conventional HTTP status codes. Error responses include a JSON body with an error string describing what went wrong.

{
  "error": "Insufficient credits"
}
400
Malformed request body or invalid JSON.
401
Missing, malformed, or revoked API key.
402
Out of credits. Top up from the pricing page.
403
Plan does not include API access, or request IP not in the key's allowlist.
404
Resource not found (for example a list or subscription that does not exist or belongs to another account).
409
Duplicate resource (for example a contact already in a list, or a duplicate webhook subscription).
422
Validation failure. Body is well-formed but one or more fields are missing or invalid.
429
Rate limit exceeded. Wait until the daily reset or upgrade your plan.
500
Server error. If you see these repeatedly, contact support.

Sandbox

Sandbox keys (piq_test_) return deterministic mock data without hitting enrichment providers or charging credits. Use them in CI, integration tests, and local development.

Sandbox responses are shaped exactly like live responses but include an extra field:

{
  "status": "VERIFIED",
  "email": "sarah.chen@stripe.com",
  "phone": [{ "number": "+1 (555) 000-0000", "type": "mobile" }],
  "job_title": "Example Title",
  "company_domain": "stripe.com",
  "confidence": 95,
  "credits_remaining": 999,
  "_sandbox": true
}

The mock email is generated from the input name + company. Phone numbers are always +1 (555) 000-0000. The _sandbox: true field is reliable: test for it in your code if you need to branch on sandbox vs live data.

POST/v1/enrich

Enrich a contact

Returns a verified work email and/or direct dial for a single person. The most common entry point to the prospiq API.

Request body

first_namestringrequired
Contact's first name. Must be 2+ characters.
last_namestringrequired
Contact's last name.
companystringrequired
Company name or domain. Must be 2+ characters. We resolve domains and aliases automatically.
typeenum
What to enrich. One of email (default), phone, or both.
fieldsstring[]
Restrict response fields. Allowed: email, phone, job_title, company_domain, confidence. If omitted, all available fields are returned.

Example request

curl https://prospiq.net/api/v1/enrich \
  -X POST \
  -H "Authorization: Bearer piq_live_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "first_name": "Sarah",
    "last_name": "Chen",
    "company": "Stripe",
    "type": "email"
  }'

Response

{
  "status": "VERIFIED",
  "email": "sarah.chen@stripe.com",
  "job_title": "Head of Growth",
  "company_domain": "stripe.com",
  "confidence": 97,
  "credits_remaining": 1499
}

Response fields

statusstring
One of VERIFIED, NOT_FOUND, or NO_CREDITS. See status values.
emailstring
Returned only when found. Always lowercase, verified deliverable.
phoneobject[]
Array of phone objects, each with number and type (mobile, direct, or office).
job_titlestring
When known.
company_domainstring
Resolved company root domain.
confidencenumber
Integer 0–100. Higher is better. Returned when status is VERIFIED.
credits_remainingnumber
Account credit balance after this request.
POST/v1/enrich/bulk

Bulk enrich

Enrich up to 50 contacts (Growth) or 100 contacts (Pro) in a single request. Processed sequentially server-side. Webhook delivery available on completion.

Request body

contactsobject[]required
Array of contact objects. Each must include first_name, last_name, and company.
typeenum
What to enrich for every contact in the batch. One of email, phone, or both. Defaults to email.
webhook_urlstring
Optional HTTPS URL. When provided, we POST a completion event here once the batch finishes. Falls back to the key's default webhook URL if configured.

Example request

curl https://prospiq.net/api/v1/enrich/bulk \
  -X POST \
  -H "Authorization: Bearer piq_live_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "email",
    "contacts": [
      { "first_name": "Sarah", "last_name": "Chen", "company": "Stripe" },
      { "first_name": "Maya",  "last_name": "Patel", "company": "Linear" },
      { "first_name": "Jordan","last_name": "Park",  "company": "Vercel" }
    ]
  }'

Response

{
  "status": "completed",
  "total": 3,
  "verified": 2,
  "not_found": 1,
  "credits_used": 2,
  "credits_remaining": 1497,
  "results": [
    {
      "first_name": "Sarah",
      "last_name": "Chen",
      "company": "Stripe",
      "status": "VERIFIED",
      "email": "sarah.chen@stripe.com",
      "job_title": "Head of Growth",
      "company_domain": "stripe.com",
      "confidence": 97
    },
    {
      "first_name": "Maya",
      "last_name": "Patel",
      "company": "Linear",
      "status": "VERIFIED",
      "email": "maya@linear.app",
      "job_title": "Software Engineer",
      "company_domain": "linear.app",
      "confidence": 92
    },
    {
      "first_name": "Jordan",
      "last_name": "Park",
      "company": "Vercel",
      "status": "NOT_FOUND",
      "company_domain": "vercel.com"
    }
  ]
}

The batch completes synchronously and the response includes all results. We also fire two webhooks (non-blocking, fire-and-forget) once a batch finishes: bulk.completed to any subscribed endpoints, andcontact.verified per verified contact. See webhooks.

GET/v1/credits

Get credit balance

Returns the current credit balance, plan, and daily usage. Use this to surface remaining credits to your users or branch logic before large bulk requests.

Example request

curl https://prospiq.net/api/v1/credits \
  -H "Authorization: Bearer piq_live_xxxxxxxxxxxx"

Response

{
  "credits": 1497,
  "plan": "growth",
  "daily_requests_used": 38,
  "daily_limit": 500
}
creditsnumber
Current enrichment credit balance.
planstring
One of free, starter, growth, or pro.
daily_requests_usednumber
Total API requests across all active keys today (UTC).
daily_limitnumber
Plan's daily request cap.
GET/v1/lists

List lists

Returns all lists in your account, ordered by pinned first, then most recently updated.

Example request

curl https://prospiq.net/api/v1/lists \
  -H "Authorization: Bearer piq_live_xxxxxxxxxxxx"

Response

{
  "lists": [
    {
      "id": "list_8f2a91",
      "name": "Q2 SaaS targets",
      "description": null,
      "color": "indigo",
      "is_pinned": true,
      "contact_count": 142,
      "created_at": "2026-05-12T09:14:00.000Z",
      "updated_at": "2026-05-13T18:22:11.000Z"
    },
    {
      "id": "list_7b4c02",
      "name": "Hot leads - May",
      "description": "Outreach for the June 23 launch",
      "color": "amber",
      "is_pinned": false,
      "contact_count": 38,
      "created_at": "2026-05-08T11:00:00.000Z",
      "updated_at": "2026-05-13T11:42:00.000Z"
    }
  ]
}
POST/v1/lists/{list_id}/contacts

Add contact to list

Adds a previously-enriched contact (identified by its search_id from a prior /v1/enrich response) to a list. Fires the contact.added_to_list webhook on success.

Path parameter

list_idstringrequired
ID of the list to add the contact to. Must belong to the authenticated account.

Request body

search_idstringrequired
The search ID from a previous enrichment response.

Example request

curl https://prospiq.net/api/v1/lists/list_8f2a91/contacts \
  -X POST \
  -H "Authorization: Bearer piq_live_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{ "search_id": "srch_a7f3e1b2" }'

Response

{
  "success": true,
  "list_id": "list_8f2a91",
  "search_id": "srch_a7f3e1b2"
}

Returns 409 Conflict if the contact is already in the list, or 404 Not Foundif either the list or the search ID don't belong to your account.

Webhooks

Webhooks deliver real-time events to your HTTPS endpoint as things happen in prospiq: contacts get verified, batches complete, credits run low. Subscribe to events once; we POST each event to your URL as it fires.

How it works

  1. Create a subscription with POST /v1/subscribe specifying the event type and target URL.
  2. When the event fires, we POST a signed JSON payload to your URL within seconds.
  3. Verify the X-ProspIQ-Signature header using your subscription secret (see signatures).
  4. Respond with any 2xx status code. Non-2xx responses count as failures.

Delivery guarantees

  • Events are also stored for 24 hours and accessible via polling as a fallback.
  • Failed deliveries are retried with backoff. Consistently-failing endpoints may be deactivated.
  • Each event is delivered with timestamp timestamp in the body. Use it for ordering.
  • Delivery is at-least-once. Make your handlers idempotent.

Delivery headers

X-ProspIQ-Signatureheader
HMAC-SHA256 of the raw request body, signed with your subscription secret.
X-ProspIQ-Eventheader
The event type, for example contact.verified.
X-ProspIQ-Testheader
Present and set to true only on deliveries from the test endpoint.

Payload shape

{
  "event": "contact.verified",
  "data": {
    "first_name": "Sarah",
    "last_name": "Chen",
    "company": "Stripe",
    "email": "sarah.chen@stripe.com",
    "phone": null,
    "job_title": "Head of Growth",
    "confidence": 97,
    "search_id": "srch_a7f3e1b2"
  },
  "timestamp": "2026-05-13T18:30:00.000Z"
}
POST/v1/subscribe

Subscribe to an event

Create a webhook subscription. Each subscription is bound to a single event type and target URL. Duplicate active subscriptions (same event + same URL) return 409 Conflict.

Request body

eventstringrequired
One of the supported event types.
target_urlstringrequired
Your HTTPS endpoint. HTTP URLs are rejected with 422.

Example request

curl https://prospiq.net/api/v1/subscribe \
  -X POST \
  -H "Authorization: Bearer piq_live_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "event": "contact.verified",
    "target_url": "https://yourapp.com/webhooks/prospiq"
  }'

Response

{
  "id": "sub_3f7a02",
  "event": "contact.verified",
  "target_url": "https://yourapp.com/webhooks/prospiq",
  "secret": "whsec_b7a23c91d4e8...",
  "active": true,
  "created_at": "2026-05-13T18:45:00.000Z"
}
Save the secret

The secret is shown only on creation. Use it to verify the X-ProspIQ-Signature on incoming webhooks. If lost, delete the subscription and create a new one.

Unsubscribing

Delete a subscription by passing its ID as a query parameter:

curl https://prospiq.net/api/v1/subscribe?id=sub_3f7a02 \
  -X DELETE \
  -H "Authorization: Bearer piq_live_xxxxxxxxxxxx"

Returns { "success": true }.

GET/v1/webhooks

List subscriptions

Returns all active webhook subscriptions on the authenticated account, plus the catalogue of available event types.

Example request

curl https://prospiq.net/api/v1/webhooks \
  -H "Authorization: Bearer piq_live_xxxxxxxxxxxx"

Response

{
  "subscriptions": [
    {
      "id": "sub_3f7a02",
      "event": "contact.verified",
      "target_url": "https://yourapp.com/webhooks/prospiq",
      "active": true,
      "consecutive_failures": 0,
      "last_triggered_at": "2026-05-13T18:30:00.000Z",
      "last_failure_at": null,
      "created_at": "2026-05-13T18:45:00.000Z"
    }
  ],
  "available_events": [
    { "event": "contact.verified", "label": "New verified contact" },
    { "event": "credits.low", "label": "Credits running low (50%)" },
    { "event": "credits.exhausted", "label": "Credits exhausted (0)" },
    { "event": "bulk.completed", "label": "Bulk enrichment job completed" },
    { "event": "contact.added_to_list", "label": "Contact added to a list" }
  ]
}
POST/v1/webhooks/test

Test a subscription

Send a sample event to a subscription's URL. Useful for validating signature verification and HTTPS reachability without waiting for a real event.

Request body

subscription_idstringrequired
ID of the subscription to test (from POST /v1/subscribe response).

Example request

curl https://prospiq.net/api/v1/webhooks/test \
  -X POST \
  -H "Authorization: Bearer piq_live_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{ "subscription_id": "sub_3f7a02" }'

Response

On successful delivery:

{
  "success": true,
  "message": "Test webhook delivered to https://yourapp.com/webhooks/prospiq",
  "status_code": 200
}

The test payload includes the same headers as a real delivery, plus X-ProspIQ-Test: true, and a _test: true field on the body.

GET/v1/webhooks/poll/{event}

Poll stored events

Returns the last 25 events of a given type from the past 24 hours. Useful as a fallback if your webhook receiver was down, or for environments where you cannot expose an inbound URL.

Path parameter

eventstringrequired
Any of the supported event types.

Query parameters

sinceISO 8601
If provided, only events with a created_at after this timestamp are returned. Use the last event's timestamp to paginate.

Example request

curl "https://prospiq.net/api/v1/webhooks/poll/contact.verified?since=2026-05-13T12:00:00Z" \
  -H "Authorization: Bearer piq_live_xxxxxxxxxxxx"

Response

{
  "events": [
    {
      "id": "evt_a8f31b",
      "event": "contact.verified",
      "payload": {
        "event": "contact.verified",
        "data": { "email": "sarah.chen@stripe.com", "...": "..." },
        "timestamp": "2026-05-13T18:30:00.000Z"
      },
      "created_at": "2026-05-13T18:30:00.000Z"
    }
  ]
}

Webhook signatures

Every webhook we deliver is signed with HMAC-SHA256. The hex digest is sent in the X-ProspIQ-Signature header. Verify it on every request: anyone who knows your endpoint URL can send fake events, but they cannot forge a signature without your secret.

Verification

Compute HMAC-SHA256 of the raw request body using your subscription secret. Compare to the header value using a constant-time comparison.

Verify a signature
# Compute the expected signature
echo -n "$REQUEST_BODY" | openssl dgst -sha256 -hmac "$WEBHOOK_SECRET" -hex
# Compare to the X-ProspIQ-Signature header value
Use the raw body

The signature is over the exact bytes we send. If your framework parses JSON before you get the raw body, you may end up signing a re-serialized version with different whitespace or key order, and verification will fail. Capture the raw request body before parsing.

Event types

These are the event types prospiq emits. Subscribe to any of them via /v1/subscribe.

contact.verified

A contact was verified through /v1/enrich or /v1/enrich/bulk.

{
  "first_name": "Sarah",
  "last_name": "Chen",
  "company": "Stripe",
  "email": "sarah.chen@stripe.com",
  "phone": null,
  "job_title": "Head of Growth",
  "confidence": 97,
  "search_id": "srch_a7f3e1b2"
}
credits.low

Your account's credit balance dropped below 50% of your plan's monthly allowance.

{
  "credits": 750,
  "plan": "growth",
  "threshold": 1500
}
credits.exhausted

Credit balance reached zero. Subsequent enrichments will return 402 until you top up.

{
  "credits": 0,
  "plan": "growth"
}
bulk.completed

A bulk enrichment job finished. Fired once per /v1/enrich/bulk call.

{
  "total": 50,
  "verified": 38,
  "not_found": 12,
  "credits_used": 38,
  "credits_remaining": 1462
}
contact.added_to_list

A contact was added to a list via the dashboard or /v1/lists/{id}/contacts.

{
  "list_id": "list_8f2a91",
  "list_name": "Q2 SaaS targets",
  "contact_email": "sarah.chen@stripe.com",
  "contact_name": "Sarah Chen",
  "company": "Stripe",
  "job_title": "Head of Growth",
  "search_id": "srch_a7f3e1b2"
}

Status values

Every enrichment response carries a status field. These are the values you should branch on.

VERIFIED
A verified email and/or phone was found. The relevant fields are populated and credits were charged.
NOT_FOUND
No verified result could be produced. No credits charged. Common when a person has left the company, the company is too small to have public data, or the name is misspelled.
NO_CREDITS
Appears only in bulk responses, per-contact. Indicates credits ran out mid-batch and this contact was skipped. No credits charged for skipped contacts.

Bulk enrichment responses additionally carry a top-level status of completed regardless of per-contact outcomes.

Ready to build?

Generate your first API key from settings. Sandbox keys are free to create on any plan.