Agent-readable docs index: /llms.txt. Full docs in one file: /llms-full.txt. Download /docs.zip to grep all markdown files locally.

Error Handling

The API uses standard HTTP status codes and returns detailed error information in a consistent format.

Error Response Format

All errors return a JSON object with the following structure:
{ "error": { "code": "error_code", "message": "Human-readable error message", "request_id": "req_abc123def456", "details": {} } }
Save the request_id from error responses. When contacting support, include this ID for faster troubleshooting.

Upgrade from free access

Paid enrichment without a subscription returns HTTP 403 with error.code: "subscription_required". Its error.details.upgrade supplies the workspace plans and checkout endpoints plus approval and payment requirements. Counts and supported estimates remain available for free.
Read GET /v1/account/plans and show the live price, currency, recurring interval, quantity and benefits. Ask the customer to approve before creating checkout. A checkout link does not activate access: the customer completes payment on Stripe, then the client checks account and usage before retrying. Declined purchases must not create checkout. A changed price list requires fetching plans again and obtaining approval for the new terms.

HTTP Status Codes

StatusDescription
200Success
201Created - Resource successfully created
400Bad Request - Invalid parameters or validation failed
401Unauthorized - Invalid, missing, or expired API key
403Forbidden - Insufficient permissions or scope
404Not Found - Requested resource doesn't exist
429Too Many Requests - Rate limit exceeded (see Rate Limits)
500Internal Server Error - Unexpected server error

Error Codes Reference

Authentication Errors (401)

CodeDescription
missing_api_keyNo API key provided in the Authorization header
invalid_auth_formatAuthorization header not using Bearer scheme
invalid_api_keyAPI key format is invalid or key doesn't exist
api_key_revokedAPI key has been revoked
api_key_expiredAPI key has expired
organization_not_foundOrganization associated with API key no longer exists

OAuth Errors (401/403)

CodeDescription
oauth_invalid_tokenOAuth access token is invalid, expired, or revoked
oauth_insufficient_scopeToken doesn't have required scopes for this endpoint

Device Auth Errors (400)

CodeDescription
authorization_pendingUser hasn't approved/denied yet (keep polling)
slow_downPolling too fast. Increase interval by 5 seconds
access_deniedUser denied the authorization request
expired_tokenDevice code has expired. Start a new flow

Validation Errors (400)

CodeDescription
validation_errorRequest body failed validation. Check details.issues
invalid_requestRequest format is invalid

Filter Errors (400)

CodeDescription
filter_not_foundNo filter matches the given filter_id. Use List Filters to discover valid slugs. details.suggestedSearchTerms gives search terms to retry with
filter_source_mismatchThe filter exists, but belongs to the other catalog — a property filter sent to a people endpoint, or vice versa. Filters cannot be mixed across sources in one request. When an equivalent filter exists on this endpoint's catalog, details.replacementFilterId names it
premium_filter_requiredThe filter is available only on plans that include premium filters
A filter_source_mismatch response identifies both sides of the mismatch:
{ "error": { "code": "filter_source_mismatch", "message": "Filter \"property_type\" belongs to the properties catalog, but this is a people search.", "request_id": "req_abc123def456", "details": { "filter_id": "property_type", "actualSourceType": "properties", "expectedSourceType": "people", "retryable": true } } }
To combine property criteria with people criteria, run the search from the property side — Search Properties with anchor: "people" and a contact_audience returns the people connected to the matching properties.

Server Errors (500)

CodeDescription
internal_errorUnexpected server error

Handling Errors

JavaScript/TypeScript

try { const response = await fetch('https://api.v2.dealmachine.com/v1/account', { headers: { 'Authorization': `Bearer ${apiKey}`, }, }); if (!response.ok) { const { error } = await response.json(); switch (error.code) { case 'invalid_api_key': console.error('Check your API key'); break; case 'validation_error': console.error('Validation issues:', error.details?.issues); break; default: console.error('Error:', error.message); } return; } const data = await response.json(); } catch (err) { console.error('Network error:', err); }

Python

import requests response = requests.get( 'https://api.v2.dealmachine.com/v1/account', headers={'Authorization': f'Bearer {api_key}'} ) if not response.ok: error = response.json()['error'] if error['code'] == 'invalid_api_key': print('Check your API key') elif error['code'] == 'validation_error': for issue in error.get('details', {}).get('issues', []): print(f"Validation error in {issue['path']}: {issue['message']}") else: print(f"Error: {error['message']}")

Validation Error Details

When you receive a validation_error, the details.issues array contains specific validation failures:
{ "error": { "code": "validation_error", "message": "Validation failed", "request_id": "req_7f8a9b0c1d2e3f4g", "details": { "issues": [ { "path": "client_id", "message": "Required" } ] } } }

Response Headers

Every response includes:
HeaderDescription
X-Request-IdUnique request ID for debugging and support