Introduction
The V2 API is the recommended version for all new integrations. It offers a cleaner architecture, standardized responses, contextual pricing, and better monitoring capabilities.
The V1 API remains fully available; there is no deprecation date. You can migrate at your own pace, endpoint by endpoint. Both versions coexist and share your workspace credits.
Why migrate?
- Standard authentication Bearer token header instead of query parameter
- Structured responses
consistent
data/error/metadata/quotasenvelope - Contextual pricing pay less for cached results and stale data
- Free Check endpoints verify data freshness before spending credits
- GDPR/CCPA compliance
automatic blocklist enforcement with clear
451status codes - Per-key rate limits finer control over API key usage alongside workspace limits
- Webhook simplification workspace-level URL or per-request override, no dedicated alert endpoints
Migration checklist
Here’s everything you need to do. Each step links to the detailed section below.
- Generate a V2 API key (
rc_prefix) from the dashboard - Switch to Bearer header authentication.
Authorization: Bearer rc_... - Update endpoint URLs. See the mapping tables
- Convert GET to POST with JSON body where applicable
- Update response parsing. Access data via
response.datainstead of root-level fields - Update error handling. Use
error.codeanderror.messageinstead oftitleandmsg - Update quota tracking. Use the new
quotas.workspaceandquotas.keystructure - Configure a webhook URL at workspace level if using async endpoints
- Test your new key with
GET /v2/usage(free) to validate connectivity - Migrate endpoint by endpoint. Both versions coexist, so you can move gradually
Tip
Start by migrating your most-used endpoint first. Once you are comfortable with the V2 response format, the remaining endpoints follow the same pattern.
Authentication
V1: Query parameter
V1 authenticates via a query parameter using keys with the sk_ prefix:
curl "https://api.reversecontact.com/v1/enrichment/profile?apikey=sk_your_api_key&linkedin_url=https://social.com/in/example"
V2: Bearer header (mandatory)
V2 uses the standard Authorization header with keys using the rc_ prefix:
curl -X POST https://api.reversecontact.com/v2/fetch/persons \
-H "Authorization: Bearer rc_your_api_key" \
-H "Content-Type: application/json" \
-d '{"url": "https://social.com/in/example"}'
Tip
Generate a V2 API key (rc_ prefix) from the API Keys page in your dashboard. Your existing sk_ keys continue to work with V1 endpoints.
Response format
V1 response
V1 mixes data fields at the root level alongside quota information:
{
"success": true,
"credits_consumed": 2,
"credits_left": 5479,
"rate_limit_left": 997,
"daily_rate_limit_left": 997,
"minute_rate_limit_left": 48,
"next_minute_rate_limit_reset": "2025-01-15T09:31:00.000Z",
"quotas": {
"credits": { "total": 10000, "used": 4521, "left": 5479 },
"daily_rate_limit": { "limit": 1000, "used": 3, "left": 997, "next_reset": "..." },
"minute_rate_limit": { "limit": 50, "used": 2, "left": 48, "next_reset": "..." }
},
"metadata": {
"source": "cache",
"request_id": "a1b2c3d4-...",
"executionTimeMs": 1234
},
"person": {
"firstName": "Jane",
"lastName": "Doe"
}
}
V2 response
V2 uses a standardized envelope with clear separation between data, errors, metadata, and quotas:
{
"success": true,
"data": {
"firstName": "Jane",
"lastName": "Doe"
},
"error": null,
"metadata": {
"requestId": "a1b2c3d4-e5f6-4890-abcd-ef1234567890",
"executionTimeMs": 1234
},
"quotas": {
"creditsConsumed": 2,
"workspace": {
"id": "ws_...",
"credits": { "total": 10000, "used": 4521, "left": 5479 },
"minuteRateLimit": { "limit": 50, "used": 2, "left": 48, "nextReset": "..." },
"dailyLimit": null,
"hasUnlimitedCredits": false,
"allowDailyOvercost": false
},
"key": {
"id": "key_...",
"dailyLimit": null,
"minuteRateLimit": { "limit": 50, "used": 2, "left": 48, "nextReset": null }
}
}
}
Key differences
| Aspect | V1 | V2 |
|---|---|---|
| Data location | Root level (e.g. response.person) |
Inside response.data |
| Request ID | metadata.request_id (snake_case) |
metadata.requestId (camelCase) |
| Data source | metadata.source ("cache" or "fresh") |
Not in body (available in logs) |
| Quota fields | Flat fields at root + nested quotas |
Structured quotas.workspace + quotas.key |
| Rate limits | Workspace-level only | Workspace + per-key limits |
Tip
When migrating your response parser, the main change is accessing response.data instead of the root-level data field (e.g. response.person, response.company).
Error handling
V1 error format
{
"success": false,
"title": "Not Found",
"msg": "No data found"
}
V2 error format
{
"success": false,
"data": null,
"error": {
"code": "REQUEST_ERROR",
"message": "Profile not found"
},
"metadata": {
"requestId": "a1b2c3d4-e5f6-4890-abcd-ef1234567890",
"executionTimeMs": 234
}
}
V2 error codes
| Status | Description |
|---|---|
400 |
Invalid JSON body or endpoint-specific validation |
401 |
Missing or invalid API key |
402 |
Insufficient credits |
403 |
API key lacks required permission, or endpoint requires a paid plan |
404 |
No result found for your query |
422 |
Schema or validation error (e.g. malformed input, disposable email, missing webhook URL) |
429 |
Rate limit exceeded. Wait and retry after nextReset |
451 |
Data Subject Blocked. Profile removed per GDPR/CCPA request |
503 |
Temporary service issue. Retry with backoff |
504 |
The lookup timed out. Retry with backoff |
For the full error reference, see Error handling & retries.
Endpoint mapping
Most V1 endpoints have a V2 equivalent. V2 standardizes all endpoints to POST with JSON body and Bearer header authentication.
Person endpoints
| V1 | V2 | Delivery |
|---|---|---|
GET /v1/enrichment/profile |
POST /v2/fetch/persons |
Sync |
POST /v2/fetch/persons/live |
Async | |
GET /v1/enrichment |
POST /v2/enrich/persons |
Sync |
POST /v1/enrichment/match |
POST /v2/enrich/persons |
Sync |
POST /v1/enrichment/persons/search |
POST /v2/search/persons |
Sync |
Company endpoints
| V1 | V2 | Delivery |
|---|---|---|
GET /v1/enrichment/company |
POST /v2/fetch/companies |
Sync |
POST /v2/fetch/companies/live |
Async | |
GET /v1/enrichment/company/domain |
POST /v2/enrich/companies |
Sync |
POST /v1/enrichment/companies/search |
POST /v2/search/companies |
Sync |
Email & contact endpoints
| V1 | V2 | Delivery |
|---|---|---|
GET /v1/enrichment/resolve/email |
POST /v2/enrich/persons |
Sync |
POST /v1/enrichment/profile-from-email |
POST /v2/enrich/persons |
Sync |
Job endpoints
| V1 | V2 | Delivery |
|---|---|---|
GET /v1/enrichment/jobs/* |
Not ported |
Utility endpoints
| V1 | V2 | Delivery |
|---|---|---|
GET /v1/workspaces/quotas |
GET /v2/usage |
Sync |
GET /v1/logs/requests |
Via Dashboard | |
GET /v1/logs/reports, POST /v1/logs/report |
Via Dashboard |
Request format changes
Here are two common examples. All other endpoints follow the same pattern; see the full Endpoint mapping above.
Fetch a person profile (live)
Before (V1):
curl "https://api.reversecontact.com/v1/enrichment/profile?apikey=sk_your_key&linkedin_url=https://social.com/in/janedoe"
After (V2):
# webhookUrl is optional if a default is configured in Settings > Webhooks
curl -X POST https://api.reversecontact.com/v2/fetch/persons/live \
-H "Authorization: Bearer rc_your_key" \
-H "Content-Type: application/json" \
-d '{"url": "https://social.com/in/janedoe"}'
Credits & pricing
V2 introduces contextual pricing: the credit cost depends on whether data comes from a live scrape or cache, and whether a result was found or not.
For the full credit breakdown per endpoint, see Rate limits & credits.
What’s new in V2
- Check endpoints free endpoints to verify data existence and freshness before spending credits.
- Company Search new company search endpoint (1 credit).
- GDPR/CCPA blocklist
automatic enforcement with
451status code. - Disposable email detection
422error when a disposable email domain is used.
Rate limiting
Both V1 and V2 enforce minute-level and daily rate limits at the workspace level.
What changes in V2
- Per-key limits V2 adds rate limits scoped to individual API keys. See API key management.
- Structured quota response
rate limit status is available in every response under
quotas.workspace.minuteRateLimitandquotas.key.minuteRateLimit. nextResetfield tells you exactly when the current minute window resets (ISO 8601 timestamp).
For the full details, see Rate limits & credits.
Async results
V2 delivers async results in two ways: pull on demand via polling (recommended, no public endpoint required), or push to a webhook URL configured in Settings > Webhooks or per-request via webhookUrl.
See the Polling guide for the recommended path with side-by-side comparison, or Webhooks for push delivery setup.