REST API and webhooks
Scrape.Email developer reference
Everything the workspace can do is available to your code: search and export the lead catalog, enrich public URLs, run Instant Scrape jobs in the cloud, read saved leads, pull music-promotion and press contacts, and receive signed account events. Jobs keep running on the server after your process exits, so integrations can run unattended.
Authentication and scopes
Create an account-scoped key in the signed-in API & Webhooks module. The full key is shown once. Send it only from server-side code.
Search permits search and saved-lead reads.
Export permits CSV export delivery.
Webhook identifies keys intended for integration administration; webhook destinations themselves are managed in the workspace.
Authorization: Bearer bl_live_...
Content-Type: application/json
Base URL: https://www.scrape.emailSearch and reveal contacts
POST /api/v1/leads/search requires the Search scope. It returns the requested page, charges only newly delivered canonical contacts, and saves delivered contacts to the same account workspace.
curl -X POST https://www.scrape.email/api/v1/leads/search \
-H "Authorization: Bearer bl_live_..." \
-H "Content-Type: application/json" \
-d '{
"industry": "Computer Software",
"country": "Canada",
"verification": "Yes",
"page": 1,
"pageSize": 100
}'{
"rows": [{ "name": "...", "email": "..." }],
"total": 1842,
"page": 1,
"pageSize": 100,
"usage": {
"creditLimit": 150000,
"creditsUsed": 100,
"creditsRemaining": 149900
},
"saved": { "saved": 100, "existing": 0 },
"matchMode": "exact",
"relaxedFilters": []
}Supported filters
companyemailphonedomaintitleindustrylocationcountryverificationminRevenuemaxRevenuelinkedintechStackfundingtrafficGrowthhiringcompanyAttributeshqLocationpage starts at 1 and pageSize can cover all matching rows available to the workspace. Remaining credits are the reveal boundary. Lead Finder uses 1 credit per lead; Music Promotion and Press use 5 credits per delivered lead. At least one targeting filter is required. When exact criteria return no rows, matchMode and relaxedFilters explain any closest-match recovery.
Core contact fields
nameemailphonelinkedintitlelevelcompanydomainlocationcountryrevenueemployeesindustryverificationverification_statusCSV export
POST /api/v1/leads/export requires the Export scope. Use the same search filters plus quantity for the number of matching rows to reveal. Delivery is bounded by matching rows and remaining account credits.
The response is UTF-8 CSV with Content-Disposition. Usage and save totals are returned in X-Lead-* response headers. Spreadsheet-formula prefixes are neutralized before export.
curl -X POST https://www.scrape.email/api/v1/leads/export \
-H "Authorization: Bearer bl_live_..." \
-H "Content-Type: application/json" \
-o verified-software-leads.csv \
-d '{
"industry": "Computer Software",
"verification": "Yes",
"quantity": 5000
}'Saved leads
GET /api/v1/leads/saved requires the Search scope and returns only contacts already revealed in this account workspace. Reading saved contacts does not spend credits.
Query parameters: page 1-10000, pageSize 1-200, optional status, and optional campaignId.
curl "https://www.scrape.email/api/v1/leads/saved?page=1&pageSize=100&status=Saved" \
-H "Authorization: Bearer bl_live_..."
{
"rows": [ ... ],
"total": 240,
"page": 1,
"pageSize": 100
}Account and credits
GET /api/v1/account requires the Search scope. It returns the workspace, plan, live credit balance, and the credit cost of every module so your integration can budget before it reveals anything.
curl https://www.scrape.email/api/v1/account -H "Authorization: Bearer bl_live_..."
{
"workspace": "Acme Growth",
"plan": "Professional",
"usage": { "creditLimit": 150000, "creditsUsed": 4200, "creditsRemaining": 145800 },
"creditCosts": {
"leadFinder": 1, "urlEnrichment": 5,
"musicPromotion": 5, "press": 5, "instantScrape": 20
}
}Email verification
POST /api/v1/verify requires the Search scope and accepts one address or up to 100 per call. It checks syntax, domain, the mail exchanger, disposable mailboxes, role addresses, free providers, common misspellings, and catalog readiness. The cost is 1 credit per verified address; only the affordable share of a batch runs and the remainder is returned in skipped.
A misspelled domain returns a suggestion so you can correct it before sending. invalid and disposable addresses should be removed from a send list; risky addresses are deliverable but personal or shared.
curl -X POST https://www.scrape.email/api/v1/verify -H "Authorization: Bearer bl_live_..." -H "Content-Type: application/json" -d '{ "emails": ["jane@company.com", "sales@gmial.com"] }'
{
"rows": [{
"email": "sales@gmial.com",
"status": "invalid",
"reason": "The domain looks like a misspelling of a major provider.",
"suggestion": "sales@gmail.com",
"score": 20,
"checks": { "syntax": true, "mx": false, "typo": true }
}],
"summary": { "total": 2, "valid": 1, "invalid": 1 },
"creditsCharged": 2
}Free signup and account deletion
POST /api/v1/register is public — no key required. It creates a 14-day trial workspace with 20 free credits and returns a working API key straight away, which is how the mobile app onboards new users. It is throttled per IP because every signup carries free credits.
POST /api/v1/account/delete is the self-service deletion path: send your key and { "confirm": true } to remove the account and all of its data. Payment records are retained for accounting. Deletion is also available at /delete-account.
# Create a free account (no key needed)
curl -X POST https://www.scrape.email/api/v1/register -H "Content-Type: application/json" -d '{ "name": "Alex Morgan", "email": "alex@company.com", "password": "at-least-eight" }'
# → { "apiKey": "bl_live_...", "account": { "plan": "Trial", "usage": { "creditsRemaining": 20 } } }
# Delete the account and all of its data
curl -X POST https://www.scrape.email/api/v1/account/delete -H "Authorization: Bearer bl_live_..." -H "Content-Type: application/json" -d '{ "confirm": true }'URL enrichment
POST /api/v1/enrichment requires the Search scope and accepts up to 100 public URLs per call. It returns the brand-owned contact routes found on each page: all emails, all phone numbers, social usernames, per-network profile URLs, the contact page, and the submission link. Off-brand social accounts, paid pitch platforms, and unrelated submission links are removed automatically. Only URLs that return contact details are charged, at 5 credits each.
curl -X POST https://www.scrape.email/api/v1/enrichment -H "Authorization: Bearer bl_live_..." -H "Content-Type: application/json" -d '{ "urls": ["https://example.com", "https://another.com"] }'{
"rows": [{
"inputUrl": "https://example.com",
"status": "enriched",
"company": "Example",
"emails": ["hello@example.com"],
"phones": ["+1 555 0100"],
"socialUsernames": ["@example · Instagram"],
"contactPage": "https://example.com/contact",
"submissionLink": "",
"socialProfiles": ["https://www.instagram.com/example/"]
}],
"creditsCharged": 5,
"contactDetailsFound": 1
}Emits enrichment.completed.
Instant Scrape — asynchronous jobs
Instant Scrape is asynchronous. POST /api/v1/scrape/jobs accepts a URL list, queues it, and returns immediately. A server-side worker then keeps advancing the job batch by batch even if your process exits, the container restarts, or nobody is watching the dashboard. Your integration can simply wait for the scrape.job.completed webhook instead of polling.
# 1. Queue a job (Search scope)
curl -X POST https://www.scrape.email/api/v1/scrape/jobs -H "Authorization: Bearer bl_live_..." -H "Content-Type: application/json" -d '{ "text": "https://www.instagram.com/natgeo/" }'
# 2. Optional: check progress (Search scope)
curl "https://www.scrape.email/api/v1/scrape/jobs/{jobId}?offset=0&limit=200" -H "Authorization: Bearer bl_live_..."
# 3. Download the finished CSV (Export scope)
curl -o scrape-job.csv "https://www.scrape.email/api/v1/scrape/jobs/{jobId}/export" -H "Authorization: Bearer bl_live_..."POST /api/v1/scrape/jobs returns the job id with usage and skipped counts. Each successful enriched result costs 20 credits; failed inputs and unsupported URLs are refunded or never charged.
GET /api/v1/scrape/jobs → the 50 most recent jobs for the account.
GET /api/v1/scrape/jobs/{jobId} → job progress plus result rows, paged with offset and limit (max 2,000).
GET /api/v1/scrape/jobs/{jobId}/export → UTF-8 CSV of the results, with X-Scrape-Rows.
Emits scrape.job.created and scrape.job.completed.
Music promotion
POST /api/v1/music-promotion/search requires the Search scope; POST /api/v1/music-promotion/export requires Export. Both accept the same filters as the workspace module: playlist, radio, TV, network, podcast, DJ, label, PR, booking, venue, and media records, with country, region, city, genre, audience market, contact route, and qualification filters. Each newly delivered canonical contact costs 5 credits and is saved once.
curl -X POST https://www.scrape.email/api/v1/music-promotion/search -H "Authorization: Bearer bl_live_..." -H "Content-Type: application/json" -d '{
"query": "college radio",
"country": "United States",
"contactMethod": "email",
"pageSize": 50
}'Press and journalists
POST /api/v1/press/search requires the Search scope; POST /api/v1/press/export requires Export. Filter by category, outlet type, country, contact route, email status, email evidence, or source provenance. Each newly delivered contact costs 5 credits.
curl -X POST https://www.scrape.email/api/v1/press/search -H "Authorization: Bearer bl_live_..." -H "Content-Type: application/json" -d '{
"category": "Technology & Software",
"country": "United Kingdom",
"contactMethod": "email",
"pageSize": 50
}'Suppression sync
Keep your do-not-contact list aligned with ours. A suppression added here is excluded from every send path, including scheduled follow-ups, and checked again immediately before delivery. Sync a suppression from your CRM so nothing is ever emailed twice.
GET /api/v1/suppressions reads the list, POST adds one or many with a reason and source, and DELETE ?email= removes an entry. All three require the Search scope.
# Read the list
curl https://www.scrape.email/api/v1/suppressions?limit=500 -H "Authorization: Bearer bl_live_..."
# Add one, or many in a single call
curl -X POST https://www.scrape.email/api/v1/suppressions -H "Authorization: Bearer bl_live_..." -H "Content-Type: application/json" -d '{ "emails": ["a@x.com", "b@y.com"], "reason": "Unsubscribed", "source": "Provider" }'
# Remove an entry
curl -X DELETE "https://www.scrape.email/api/v1/suppressions?email=a@x.com" -H "Authorization: Bearer bl_live_..."Signed webhooks
Register an HTTPS destination in the workspace and subscribe to any of: search.completed, export.completed, lead.created, enrichment.completed, scrape.job.created, scrape.job.completed, music-promotion.completed, and press.completed. Each delivery uses a unique event ID. Failed deliveries are attempted up to three times; a destination pauses after five failed delivery cycles.
Event payloads
| Event | Fired when | data |
|---|---|---|
| search.completed | A lead search delivered contacts | delivered, total, page, pageSize, matchMode, creditsRemaining |
| export.completed | A CSV export finished or a scrape CSV was downloaded | rowCount, saved, existing, creditsRemaining, jobId, source |
| lead.created | New canonical contacts were saved to the workspace | count, source |
| enrichment.completed | URL enrichment finished | submitted, contactDetailsFound, contactDetailsLocked, creditsCharged, creditsRemaining |
| scrape.job.created | An Instant Scrape job was queued | jobId, datasetId, inputCount, totalBatches, skipped, creditsRemaining |
| scrape.job.completed | The cloud worker finished a scrape job | jobId, inputs, success, failed, creditsUsed |
| music-promotion.completed | A music-promotion reveal delivered contacts | delivered, total, creditsCharged, creditsRemaining, source |
| press.completed | A press reveal delivered contacts | delivered, total, creditsCharged, creditsRemaining, source |
X-Scrape-Email-Event: search.completed
X-Scrape-Email-Signature: t=1700000000,v1=<hex digest>
Content-Type: application/json
{
"id": "evt_...",
"event": "search.completed",
"timestamp": 1700000000,
"data": { ... }
}Compute HMAC-SHA256 with the signing secret over timestamp + "." + rawBody, then compare it to v1 using a constant-time comparison.
Reject stale timestamps and store processed event IDs to prevent replay.
Return a 2xx response quickly, then process the accepted event asynchronously.
Rotate a signing secret from the workspace when exposure is suspected; the previous value stops working immediately.
Errors, usage, and rate limits
| HTTP | Meaning |
|---|---|
| 400 | Invalid JSON, filter, page, page size, or export quantity. |
| 401 | The Bearer key is missing, invalid, revoked, or lacks the required scope. |
| 402 | The account has no remaining credits for this delivery. |
| 429 | The per-key endpoint limit was exceeded. Honor Retry-After. |
| 503 | The catalog or delivery service is temporarily unavailable. |
Per API key: search permits 60 requests per minute, export 10, saved-lead reads 120, account reads 120, enrichment 30, music and press search 30, instant-scrape creation 20, and instant-scrape status 240. Successful and rate-limited responses include RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset. A 429 also includes Retry-After. Use exponential backoff for 429 and transient 503 responses; do not automatically retry validation or credit errors.
SDKs and generated clients
Every client is generated from the same OpenAPI contract, so the SDKs cannot drift from the API. The first-party TypeScript SDK ships the typed client plus a webhook signature verifier; other languages can be generated from the spec with openapi-generator.
# Official TypeScript SDK
npm install scrape-email
// or generate a client for any language
npx @openapitools/openapi-generator-cli generate -i https://www.scrape.email/openapi.json -g python -o ./clients/pythonimport { ScrapeEmail } from "scrape-email";
const client = new ScrapeEmail({ apiKey: process.env.KEY! });
const { rows, usage } = await client.searchLeads({
industry: "Computer Software",
country: "Canada",
pageSize: 100,
});
const { job } = await client.createScrapeJob(urls);
const done = await client.waitForScrapeJob(job.id);Need implementation help?
Use Help for workflow guidance or open API & Webhooks after signing in to create credentials and test a destination.