01 Overview
ReachAPI is a REST‑oriented service suite for embedding contact and company data into applications. It returns structured JSON, and the data‑retrieval endpoints follow an asynchronous, credit‑metered pattern: you submit filter criteria, the server validates and processes matching records in the background, and you either poll for a batch ID or receive a webhook when the batch is ready.
Base URL
All endpoints below are relative to this base URL.
https://api.reachstream.com
Every endpoint on this page is also described machine‑readably in an OpenAPI 3.0 spec — YAML or JSON — import it into Postman/Insomnia, feed it to a codegen tool, or point an LLM agent at it directly.
02 Quickstart
The full request cycle in four calls: check your credit balance, size a search with Counts, submit a Data Filter request, then poll for the results. Grab an API key from Authentication first — every call below expects it in the X-API-Key header.
Step 1 — Check your credit balance
Confirms your key works and shows how much credit you have before spending any of it.
curl --location 'https://api.reachstream.com/api/v2/retrive/users/active/credits' \ --request POST \ --header 'X-API-Key: your-api-key-here'
Step 2 — Size your search with Counts
Try a filter and see how many records match — this doesn't spend download credits.
curl --location 'https://api.reachstream.com/api/v2/request/records/count' \
--request POST \
--header 'X-API-Key: your-api-key-here' \
--header 'Content-Type: application/json' \
--data '{
"filter": {
"company_address_state": { "0": "California" }
}
}'
See Filter Property for the full set of filterable fields. Full reference →
Step 3 — Submit a Data Filter request
Once the filter looks right, submit it with a fetchCount to start processing. This returns a batch_process_id — it doesn't return records yet.
curl --location 'https://api.reachstream.com/api/v2/async/records/filter/data' \
--request POST \
--header 'X-API-Key: your-api-key-here' \
--header 'Content-Type: application/json' \
--data '{
"fetchCount": 50,
"filter": {
"company_address_state": { "0": "California" }
}
}'
Step 4 — Poll for the results
Poll with the batch_process_id from Step 3 until it stops returning "still being processed" — then the validated records are in the response.
curl --location 'https://api.reachstream.com/api/v2/records/batch-process?batch_process_id=31' \ --header 'X-API-Key: your-api-key-here'
03 Authentication
API Key
Every request (except the webhook receiver on your own server) is authenticated with an X-API-Key header.
| Step | Action |
|---|---|
| 1 | Log in to your ReachStream account. |
| 2 | Open settings icon (bottom left) → Account Details → API. |
| 3 | Click Generate API Key. |
| 4 | Copy and store the key securely. |
Webhook Secret
For the webhook‑based data request flow, generate a Webhook Secret Key from the same Account Details page and set an active Endpoint URL that accepts POST requests.
04 Credit Insights API
Check the available credits on your account before running a large batch.
05 Counts API
Get a total match count and a small sample of records for a given filter, without spending download credits — useful for sizing a request before committing to it.
06 Data Access API
Data can be retrieved two ways: API Query (poll for results using a Batch Process ID) or Webhook (ReachStream pushes the result to your endpoint when ready). Only validated records are returned and billed — this includes both valid and catch‑all email addresses; if 99 of 100 requested contacts come back valid or catch‑all, you receive and are charged for 99.
403 Forbidden; paid plans needing more should split the job into multiple requests.Step 1 — Data Filter API
Submits filter criteria and a fetchCount. Processing happens asynchronously; the response gives you a Batch Process ID to poll.
| Field | Type | Required | Description |
|---|---|---|---|
| fetchCount | Integer | Yes | Number of records to fetch. Capped per plan — see Record Limits. |
| filter | Object | Yes | Filtering criteria — see Filter Property. |
Step 2 — Retrieve Data API
Poll with the Batch Process ID from step 1 until status is ready, then read the validated records.
| Status | Message | Meaning |
|---|---|---|
| 200 | Success | Records returned. |
| 200 | No valid email addresses were found | Widen filters or raise fetchCount. |
| 400 | Records are still being processed | Not ready yet — poll again shortly. |
| 400 | Provide a valid batch_process_id | ID malformed or unknown. |
Batch List API
Paginated audit log of every batch you've created via the Filter API — status, record counts, credits used, and the original filter.
| Field | Required | Description |
|---|---|---|
| page | No | Page number, starting at 1. |
| pageSize | No | Records per page — commonly 15, 50, 100. |
| recordStatus | No | INITIATED · PROCESSING · READY · INSUFFICIENT_CREDITS |
| order | No | asc or desc. |
Webhook‑based Request
Same filter/fetchCount payload, but instead of polling, ReachStream pushes the validated batch to your Webhook URL once ready. Requires an active webhook and its secret key in the webhook-secret-key header — this header is validated against the secret key on file for your account: omit it and you get 400, send the wrong value and you get 401.
fetchCount is capped per plan — see Record Limits.
07 Data Enrichment APIs
Send partial contact or company records in, get enriched records back — also processed asynchronously in two steps.
Initiate Enrichment
| Field | Description |
|---|---|
| enrichment_type | "CONTACT" or "COMPANY". |
| data | Array of partial contact or company objects — all fields optional, more data improves match accuracy. Capped per plan — see Record Limits. |
Each object in data is validated individually before processing — a failing row returns 400 and rejects the whole batch.
| Applies to | Requirement | Error (400) if missing |
|---|---|---|
| Every row | Must be a JSON object. | "Record at index X must be an object." |
enrichment_type: "COMPANY" | company_company_name OR company_domain. | "Record at index X must include company_company_name or company_domain." |
enrichment_type: "CONTACT" (default) | contact_first_name AND contact_last_name AND company_company_name. | "Record at index X must include contact_first_name, contact_last_name, and company_company_name." |
Retrieve Enrichment Results
08 Filter Value Reference
Use this endpoint to retrieve the supported values available for ReachAPI filters, including job titles, locations, industries, company types, technologies, employee sizes, revenue ranges, and other searchable attributes. These values can be used when building filter requests across the Counts API and Data Access APIs, helping ensure your queries use recognized filter inputs.
| Parameter | Description |
|---|---|
| job_title | Filter results by job title. |
| sic_code | Filter results by SIC code. |
| address_zipcode | Filter results by address zip code. |
| address_city | Filter results by address city. |
| address_state | Filter results by address state. |
| address_country | Filter results by address country. |
| company_name | Filter results by company name. |
| website | Filter results by website. |
| tech_keywords | Filter results by technology keywords. |
| job_title_level | Filter results by job title level. |
| job_dept_name | Filter results by job department name. |
| job_function_name | Filter results by job function name. |
| company_type | Filter results by company type. |
| company_industry_categories_list | Filter results by industry categories. |
| company_buzzwords_list | Filter results by company buzzwords. |
| employee_size | Filter results by employee size. |
| annual_revenue_amount | Filter results by annual revenue amount. |
Filter value reference lists (the full valid vocabulary for each field) are also published as an SDK on GitHub.
09 Filter Property
The filter object is the shared search payload used by the Counts, Data Access, and Data Enrichment endpoints. Every field takes an object keyed by index ("0", "1", …) so you can pass multiple values per criterion.
job_title: {"0":"CEO","1":"CFO"} matches a record whose title is CEO or CFO, so the result set can contain both CEOs and CFOs together. Different fields in the same filter object are combined with AND — e.g. a job_title filter plus a company_address_country filter matches only records satisfying both.
| Field | Type | Description |
|---|---|---|
| job_title | String | One or more job titles, e.g. "Business Manager". |
| job_title_level | String | Seniority tier, e.g. "Manager", "C-Suite". |
| job_dept_name | String | Department name, e.g. "Marketing". |
| job_function_name | String | Job function, e.g. "Engineering". |
| company_industry_categories_list | String | Industry category, e.g. "Information Technology". |
| company_buzzwords_list | String | Company buzzword, e.g. "Railway Engineering". |
| sic_code | String (numeric code) | One or more SIC codes. |
| company_employee_size | String (range) | Range string, e.g. "10 to 50". |
| company_annual_revenue_amount | String (range) | Range string, e.g. "$1M to $5M". |
| company_address_country | String | Country name. |
| company_address_zipcode | String | Zip / postal code. |
| company_address_state | String | State / province. |
| company_address_city | String | City. |
| company_name | String | Company name. |
| company_website | String | Company website. |
| company_type | String | e.g. "Government Agency", "Nonprofit", "Partnership", "Public", "Educational", "Private", "Self-Owned", "Self-Employed". |
| tech_keywords | String | Technology keyword, e.g. "amazon ec2". |
{
"filter": {
"job_title": { "0": "Business Manager" },
"job_title_level": { "0": "Manager" },
"company_address_country": { "0": "United States" },
"company_address_state": { "0": "California" },
"sic_code": { "0": "111", "1": "112" },
"company_employee_size": { "0": "10 to 20" }
}
}
Excluding values from a field
Any field in the filter object accepts a nested exclude object, keyed by index the same way as the field itself. It removes matching records from that field's results — combine it with the field's own indexed values to include a broad match while excluding specific values.
{
"filter": {
"job_title": {
"0": "Software",
"exclude": {
"0": "Software Developer"
}
}
}
}
Here, records matching "Software" are included, except those matching "Software Developer".
/api/v2/async/records/filter/data), and the Webhook‑based Filter API (/api/v2/async/records/filter).Matching related job titles
The job_title field accepts an optional matchRelatedTitles boolean, keyed alongside the field's own indexed values (not nested inside exclude). Set it to false to match only the exact title(s) supplied. Set it to true — or omit it — to also match titles ReachStream considers related to the ones supplied.
{
"filter": {
"job_title": {
"0": "Hr manager",
"1": "Project manager",
"exclude": {
"0": "Business Manager and HR",
"1": "HR Manager and Accounting Supervisor"
},
"matchRelatedTitles": false
}
}
}
true. If matchRelatedTitles is omitted, related titles are matched by default. Set it to false for an exact‑title‑only match. Supported on the Counts API and the Data Access API.10 Webhook
Once configured and active, ReachStream POSTs a JSON payload to your endpoint as soon as a batch is validated, instead of you polling for it.
Payload shape
{
"unique_processing_id": 1300,
"event": "data_notification",
"data": [
{
"id": "xxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"naics_code": [
"541330"
],
"sic_code": [
"8711"
],
"company_company_name": "xxxxx",
"company_domain": "xxxxx.com",
"company_type": "Private",
"company_employee_size": "100 to 250",
"company_annual_revenue_amount": "$10M to $25M",
"company_address_city": "xxxxx",
"company_address_state": "xxxxx",
"company_address_country": "Switzerland",
"company_phone": "xxxxx",
"company_industry_categories_list": [
"Engineering Services"
],
"company_tech_keywords_list": [
"Aws",
"Abacus",
"Azure",
"Google analytics",
"Director",
"Compliance",
"Continuous improvement",
"Financial",
"Infrastructure"
],
"company_buzzwords_list": [
"Railway Engineering",
"Power Supply",
"Rolling Stock",
"Traction Systems",
"Infrastructure"
],
"contact_name": "xxxxx",
"contact_first_name": "xxxxx",
"contact_last_name": "xxxxx",
"contact_job_title": "xxxxx",
"contact_job_title_level": "C-Suite",
"contact_job_dept_name": "C-Suite",
"contact_job_function_name": "Executive",
"contact_email": "xxxxx@xxxxx.com",
"contact_email_verified_status": "valid"
}
]
}
Delivery headers
Every delivery POST to your Webhook URL carries these headers, in addition to the JSON body above:
| Header | Value |
|---|---|
Content-Type | application/json |
p_id | The batch's unique_processing_id — same value as in the body, provided as a header too so you can route/log without parsing the payload. |
webhook-secret-key | Your account's Webhook Secret Key — use this to verify the delivery actually came from ReachStream. See Security. |
Handling incoming requests
| Step | Action |
|---|---|
| 1 | Accept POST requests at your configured endpoint URL. |
| 2 | Parse the JSON payload. |
| 3 | Process the data (e.g. write to your database). |
| 4 | Respond with 200 OK to acknowledge receipt — anything else triggers a retry. |
Retry policy
A delivery only counts as successful when your endpoint returns 200 (or a JSON body containing "status": 200). Anything else — a non‑200 status, a timeout, or an unparseable response — is logged as a failed attempt and retried on ReachStream's next delivery pass.
| Limit | Value |
|---|---|
| Max total attempts | 21 per batch. |
| Max attempts per day | 3 per batch, on a rolling 24‑hour window. |
| Retry window | 7 days from when the batch first became ready — attempts stop after this even if the 21‑try limit hasn't been reached. |
Each failed attempt triggers a notification email to your account with the error status and reason. If a batch exhausts its retries or its 7‑day window without a successful delivery, it's marked dormant, a final "try limit exceeded" email is sent, and no further attempts are made — check the Batch List API or your inbox if you suspect a delivery was missed.
Security
Every delivery includes your Webhook Secret Key in the webhook-secret-key header — before trusting a request, compare that header's value against the secret key stored in your own systems (from Account Details → API) using a constant‑time comparison, and reject anything that doesn't match. Also serve the endpoint over HTTPS only, and consider IP‑whitelisting ReachStream's outbound addresses.
Testing & troubleshooting
Use Postman or ngrok to simulate deliveries locally. If deliveries seem to fail, check your server logs, confirm the payload shape matches what's above, confirm your endpoint truly returns 200, and check for a retry‑failure email — see Retry policy.
11 HTTP Return Codes
12 Rate Limitations
HTTP 429. Build in backoff and avoid tight polling loops when waiting on batch status.