Extract Data with the Jobs API

One create/poll/result contract over every extraction endpoint: pick the work with type, track it by job_id, fetch the result when it is ready.

/api/v1
Method Path Purpose
POST /api/v1/jobs Create job
GET /api/v1/jobs/{job_id} Get status (polling)
GET /api/v1/jobs/{job_id}/result Fetch final result
Base URL: https://api.scrupp.com. A job is the same task as a direct call to the underlying endpoint — same credits, same settlement.

Create job

POST /api/v1/jobs
202
Authorization: Bearer YOUR_API_KEY
Idempotency-Key: <uuid>
Content-Type: application/json
Any of the three auth forms works: Authorization: Bearer, X-API-Key, or ?api_key=. Idempotency-Key makes a retried create return the original job instead of paying twice — reuse the same key for 24h.
{
  "type": "contact.decision_maker.linkedin",
  "input": { "items": ["https://linkedin.com/company/openai"] },
  "callback_url": "https://hooks.zapier.com/hooks/catch/123/abc",
  "metadata": { "source": "zapier", "external_id": "row_9481" }
}
input takes the same body as the equivalent per-path endpoint. metadata is stored and echoed back on status and result.
{
  "job_id": 918273,
  "status": "queued",
  "created_at": "2026-09-07T11:30:00+00:00",
  "poll_url": "/api/v1/jobs/918273",
  "result_url": "/api/v1/jobs/918273/result",
  "metadata": { "source": "zapier", "external_id": "row_9481" }
}
Replaying an Idempotency-Key returns the original job with 200 instead of 202. Reusing one with a different body is a 409 idempotency_key_reused.
curl -X POST "https://api.scrupp.com/api/v1/jobs" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Idempotency-Key: 7c4a8d09-ca37-4f0b-9a1d-2b1f5c3e9a10" \
  -H "Content-Type: application/json" \
  --data '{
    "type": "sales_navigator.search",
    "input": { "url": "https://www.linkedin.com/sales/search/people?query=...", "with_emails": true, "max": 100 }
  }'

Poll status

GET /api/v1/jobs/{job_id}
200
{
  "job_id": 918273,
  "status": "running",
  "progress": 35,
  "records": 35,
  "cost": "Calculating...",
  "retry_after": 5,
  "error": null,
  "metadata": null,
  "result_url": "/api/v1/jobs/918273/result"
}
Wait retry_after seconds between polls to keep your task-operation count down. progress is null until the job reports a target size.
  • queued → accepted, not started
  • running → processing
  • succeeded → result ready
  • failed → see error

A failed job is refunded in full: the upfront credit hold is released and cost settles at 0.

Pass callback_url when creating the job and Scrupp posts to it on completion, so Zapier/Make/n8n runs do not burn operations on polling. See Webhooks.

Get result

GET /api/v1/jobs/{job_id}/result
200
{
  "job_id": 918273,
  "status": "succeeded",
  "data": {
    "items": [
      { "name": "John Doe", "title": "CMO", "email": "john@openai.com" }
    ]
  },
  "metadata": { "source": "zapier", "external_id": "row_9481" }
}
If the job has not finished, the response is 409 with error code NOT_READY and a retry_after — treat it as "keep waiting", not as a failure.
HTTP/1.1 409 Conflict

{
  "job_id": 918273,
  "status": "running",
  "error": { "code": "NOT_READY", "message": "Job has not finished yet." },
  "retry_after": 5
}

Job types

Every type maps to an endpoint documented in the API groups; input is that endpoint's body.
Mapping
type Description Equivalent endpoint
sales_navigator.search Export a Sales Navigator search POST /sn/search
linkedin.search Export a LinkedIn search POST /linkedin/search
apollo.search Run an Apollo search POST /apollo/search
linkedin.profile Enrich LinkedIn profiles POST /linkedin/profile
company.linkedin Company lookup by LinkedIn URL POST /company/linkedin
company.domain Company lookup by domain POST /company/domain
company.name Company lookup by name POST /company/name
contact.decision_maker.linkedin Decision makers by LinkedIn URL POST /contact/decision-maker/linkedin
contact.decision_maker.name Decision makers by company name POST /contact/decision-maker/name
contact.decision_maker.domain Decision makers by domain POST /contact/decision-maker/domain
email.initials Find emails in bulk from name + domain POST /email/initials
email.linkedin Enrich LinkedIn profiles and find emails POST /email/linkedin

An unrecognised type returns 400 unknown_type and lists every supported value in the message, so you never have to guess.

5,000+
sales teams
4.8/5
G2 & Capterra
200M+
leads exported
65%
avg email find rate

Start exporting leads today

Free plan available. No credit card required. Export leads with verified emails and phones.