Getting started
Skrap API documentation
Verify emails, find work emails, search leads and run bulk jobs from your own code. JSON over HTTPS with one API key header — the same engine and credit rules as the Skrap web app.
- QuickstartMake your first request in under a minute.
- Verify an emailValid, risky, invalid or unknown — with the checks behind it.
- Find a work emailFrom a name and a company or domain.
- Bulk jobsUp to 25,000 rows per job, results as JSON or CSV.
- WebhooksSigned events when jobs finish or credits run low.
- MCP for AI agentsUse Skrap from Claude, ChatGPT and Cursor.
Quickstart
- 1
Create an API key
In the app, open Settings → API keys & MCP and create a key (paid plans). Export it as
SKRAP_API_KEYso the samples below run as-is. - 2
Verify your first email
Base URL
https://api.skrap.ai. Send JSON, get JSON.curl -X POST "https://api.skrap.ai/api/v2/verify-email" \ -H "x-api-key: $SKRAP_API_KEY" \ -H "Content-Type: application/json" \ -d '{"email":"[email protected]"}' - 3
Read the result
Branch on
status. You are charged only forvalidandriskyanswers.Response · 2001{2 "email": "[email protected]",3 "status": "valid", // valid | risky | invalid | unknown4 "retryable": false, // true: unknown for a temporary reason — try later5 "domainInfo": { "domain": "example.com", "hasMxRecords": true },6 "score": 95, // 95 valid · 10–70 risky (by risk reason) · 0 invalid · null unknown7 "checks": {8 "syntax": true,9 "mxFound": true,10 "smtp": "accepted", // accepted | rejected | inconclusive | not_checked11 "catchAll": false,12 "disposable": false,13 "roleAccount": false,14 "freeProvider": false15 },16 "riskReasons": [], // risky only: catch_all, catch_all_unconfirmed, role_account, disposable17 "didYouMean": null // typo hint, e.g. "[email protected]" for "[email protected]"18}
Authentication
Every request carries your secret key in the x-api-key header.
Header
x-api-key: sk_live_…- Create a key in the app under Settings → API keys & MCP (paid plans). Keys are shown in full only to you; store them like passwords.
- Send it on every request in the x-api-key header. Keys on a team spend the team's shared credits.
- Optional per-key controls: an IP allowlist (exact IPs or IPv4 ranges such as 10.0.0.0/8) and an expiry date. Requests from other IPs get 403 IP_NOT_ALLOWED; expired keys get 401 API_KEY_EXPIRED.
Keep keys server-side
Never ship an API key in browser or mobile code. Call Skrap from your backend, or use an integration.Credits
Credits are shared with the web app. You only pay for useful results.
| Action | Cost |
|---|---|
| Verify — Valid or Risky | 1 credit |
| Verify — Invalid or Unknown | Free |
| Find — email found (Valid or Risky) | 1 credit |
| Find — not found | Free |
| Lead & company search | Free |
| Email of a search result | 1 credit when found |
| Reverse email lookup — person found | 2 credits |
| Person enrichment — profile found (+ work email) | 1 credit (+1) |
| Company enrichment — company found (+ headcount breakdown) | 1 credit (+1) |
| Company contacts | 1 credit per person |
| Enrichment — nothing found | Free |
GET /api/v2/account returns the key owner's plan and spendable credits — a good "test connection" call.Rate limits
| Plan | Limit |
|---|---|
| Free | 30 requests / minute per key |
| Professional | 120 requests / minute per key |
| Enterprise | 600 requests / minute per key |
| Custom | Agreed with sales |
Responses carry standard rate-limit headers. Over the limit you get 429 RATE_LIMITED with a Retry-After header — wait that many seconds and retry.
Verification statuses
| Status | Meaning |
|---|---|
| valid | The mailbox exists and accepts mail. |
| risky | Deliverable but uncertain — catch-all domains, full mailboxes, mail servers that would not confirm the mailbox, and best guesses in the company's email format. Send with care. |
| invalid | The address or domain cannot receive mail. Free. |
| unknown | We could not confirm the domain has a mail server (its DNS did not answer), or there was no address to give. Free. When retryable is true, the cause was temporary (try again later). |
Errors
Errors use HTTP status codes and a JSON body with a stable code you can branch on.
Error · 402
1{2 "error": "Not enough credits",3 "code": "INSUFFICIENT_CREDITS",4 "details": { "required": 1, "available": 0 }5}| HTTP | Code | Meaning |
|---|---|---|
| 400 | VALIDATION_ERROR | A field is missing or malformed; the message says which. |
| 401 | INVALID_API_KEY | Missing, unknown or revoked key. |
| 401 | API_KEY_EXPIRED | The key passed its expiry date. |
| 402 | INSUFFICIENT_CREDITS | Not enough credits (details.required / details.available). |
| 402 | MEMBER_CREDIT_LIMIT | Your team admin's monthly limit for your account is used up. |
| 403 | PLAN_REQUIRED | The feature needs a higher plan (requiredPlan in the body). |
| 403 | IP_NOT_ALLOWED | The key's IP allowlist does not include the caller. |
| 404 | NOT_FOUND | Unknown id (task, list, webhook). |
| 429 | RATE_LIMITED | Your plan's per-minute limit for this key; wait for the Retry-After header. |
| 429 | RATE_LIMIT_EXCEEDED | Too many requests at once (concurrency) or from one IP; back off and retry. |
| 429 | ENRICH_DAILY_LIMIT | Your account's daily enrichment credit limit is used up; it resets at midnight UTC. |
| 503 | ENRICH_UNAVAILABLE | Enrichment is busy or briefly unavailable. Nothing was charged; retry later (Retry-After). |
| 5xx | INTERNAL_ERROR | Our side. Retry with back-off; nothing was charged. |