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.
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/v1Conventions
- 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 asv2.
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.
curl https://prospiq.net/api/v1/credits \
-H "Authorization: Bearer piq_live_xxxxxxxxxxxxxxxxxxxxxxxx"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.
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.
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"
}400401402403404409422429500Sandbox
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.
/v1/enrichEnrich 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_namestringrequiredlast_namestringrequiredcompanystringrequiredtypeenumemail (default), phone, or both.fieldsstring[]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
statusstringemailstringphoneobject[]number and type (mobile, direct, or office).job_titlestringcompany_domainstringconfidencenumbercredits_remainingnumber/v1/enrich/bulkBulk 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[]requiredfirst_name, last_name, and company.typeenumemail, phone, or both. Defaults to email.webhook_urlstringExample 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.
/v1/creditsGet 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
}creditsnumberplanstringfree, starter, growth, or pro.daily_requests_usednumberdaily_limitnumber/v1/listsList 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"
}
]
}/v1/lists/{list_id}/contactsAdd 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_idstringrequiredRequest body
search_idstringrequiredExample 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
- Create a subscription with
POST /v1/subscribespecifying the event type and target URL. - When the event fires, we POST a signed JSON payload to your URL within seconds.
- Verify the
X-ProspIQ-Signatureheader using your subscription secret (see signatures). - 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
timestampin the body. Use it for ordering. - Delivery is at-least-once. Make your handlers idempotent.
Delivery headers
X-ProspIQ-SignatureheaderX-ProspIQ-Eventheadercontact.verified.X-ProspIQ-Testheadertrue 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"
}/v1/subscribeSubscribe 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
eventstringrequiredtarget_urlstringrequired422.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"
}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 }.
/v1/webhooksList 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" }
]
}/v1/webhooks/testTest 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_idstringrequiredPOST /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.
/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
eventstringrequiredQuery parameters
sinceISO 8601created_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.
# Compute the expected signature
echo -n "$REQUEST_BODY" | openssl dgst -sha256 -hmac "$WEBHOOK_SECRET" -hex
# Compare to the X-ProspIQ-Signature header valueThe 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.verifiedA 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.lowYour account's credit balance dropped below 50% of your plan's monthly allowance.
{
"credits": 750,
"plan": "growth",
"threshold": 1500
}credits.exhaustedCredit balance reached zero. Subsequent enrichments will return 402 until you top up.
{
"credits": 0,
"plan": "growth"
}bulk.completedA 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_listA 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.
VERIFIEDNOT_FOUNDNO_CREDITSBulk enrichment responses additionally carry a top-level status of completed regardless of per-contact outcomes.