Skip to main content

Async API Request ID Correlation - January 2026

New requestId field for async endpoints enables reliable correlation between API requests and webhook responses.

Written by Charles Parra
Updated over 3 months ago

Async API Request ID Correlation Release Notes - January 2026

Release Date: January 2026

Affected Endpoints:
- POST /api/v1/property/search/async
- POST /api/v1/property/lookup/async
- POST /api/v1/property/skip-trace/async
- POST /api/v1/phone/verification/async
- POST /api/v1/phone/dnc/async
- POST /api/v1/phone/tcpa/async


Overview

This release introduces Request ID Correlation for all async API endpoints, enabling you to reliably match async requests with their webhook responses. Previously, when using async endpoints, correlating the webhook callback to its original request required custom tracking logic. Now, every async request returns a unique requestId that is echoed back in the webhook response.


What's New

1. Immediate Request ID in Async Responses

All async endpoints now return a requestId in the immediate HTTP response. This ID uniquely identifies your request and will match the meta.requestId in the webhook callback.

Before:

{
  "status": {
    "code": 200,
    "text": "OK"
  }
}

After:

{
  "status": {
    "code": 200,
    "text": "OK"
  },
  "requestId": "01KEVP1WBGPDR87GMRPSHWK131"
}

2. ULID Format for Request IDs

All requestId values now use the ULID (Universally Unique Lexicographically Sortable Identifier) format:

Property

Description

Length

26 characters

Character Set

Uppercase alphanumeric (Crockford Base32)

Example

01KEVP1WBGPDR87GMRPSHWK131

Sortable

Yes - ULIDs sort lexicographically by creation time

Uniqueness

Guaranteed unique across all requests

Benefits of ULID format:
- Time-ordered: IDs created later sort after earlier IDs
- URL-safe: No special characters
- Case-insensitive: Works reliably in any system
- Debuggable: Timestamp extractable for troubleshooting

3. Self-Contained Webhook Responses

Async webhook responses now include all context needed to process results without looking up the original request:

  • Request metadata echoed in the response

  • Search criteria included for search endpoints

  • Original request data returned for correlation

Example Webhook Payload:

{
  "results": {
    "properties": [...],
    "meta": {
      "results": {
        "requestCount": 1,
        "matchCount": 1,
        "noMatchCount": 0,
        "errorCount": 0
      },
      "performance": {
        "totalRequestTime": 1234,
        "startTime": "2026-01-13T14:06:08.465Z",
        "endTime": "2026-01-13T14:06:09.699Z"
      },
      "requestId": "01KEVP1WBGPDR87GMRPSHWK131",
      "apiVersion": "6.72.0"
    },
    "searchCriteria": {
      "query": "Phoenix, AZ",
      "building": {
        "bedrooms": { "min": 3 }
      }
    }
  }
}


How to Use Request ID Correlation

Basic Workflow

1. Send async request
   POST /api/v1/property/search/async2. Receive immediate response with requestId
   { "status": {...}, "requestId": "01KEVP1WBGPDR87GMRPSHWK131" }3. Store requestId with your internal job/order ID4. Webhook arrives with results
   { "results": { "meta": { "requestId": "01KEVP1WBGPDR87GMRPSHWK131" }, ... } }5. Match webhook to original request using requestId

Implementation Example

Python Example:

import requests
import json# Track pending requests
pending_requests = {}def send_async_search(search_criteria, internal_job_id):
    """Send async search and track the request ID"""
    response = requests.post(
        "https://api.batchdata.com/api/v1/property/search/async",
        headers={
            "Authorization": f"Bearer {API_TOKEN}",
            "Content-Type": "application/json"
        },
        json={
            "searchCriteria": search_criteria,
            "webhookUrl": "https://your-webhook.com/callback"
        }
    )    data = response.json()
    request_id = data.get("requestId")    # Store mapping of API request ID to your internal ID
    pending_requests[request_id] = {
        "internal_job_id": internal_job_id,
        "search_criteria": search_criteria,
        "sent_at": datetime.now()
    }    return request_iddef handle_webhook(webhook_payload):
    """Process webhook callback and match to original request"""
    request_id = webhook_payload["results"]["meta"]["requestId"]    # Find the original request
    original_request = pending_requests.get(request_id)
    if original_request:
        internal_job_id = original_request["internal_job_id"]
        # Process results for your internal job
        process_results(internal_job_id, webhook_payload["results"])
        # Clean up
        del pending_requests[request_id]


Breaking Change: Skip Trace V3

Removed Fields

The following fields have been removed from Skip Trace V3 (/api/v3/property/skip-trace) responses:

Removed Field

Replacement

inputId

Use requestId for correlation

inputIndex

Use array index position for ordering

Migration Guide

Before (deprecated):

{
  "results": {
    "data": [
      {
        "inputId": "custom-id-123",
        "inputIndex": 0,
        "contacts": [...]
      }
    ]
  }
}

After:

{
  "results": {
    "data": [
      {
        "input": {
          "requestId": "custom-id-123"
        },
        "contacts": [...]
      }
    ],
    "meta": {
      "requestId": "01KEVP28S5B241XVJR5AN4PS0V"
    }
  }
}

Migration steps:
1. Replace inputId references with input.requestId
2. Use array index (results.data[0], results.data[1], etc.) instead of inputIndex
3. Update any code that parses the deprecated field locations


Per-Item Request ID for Property Lookup

The Property Lookup API (/api/v1/property/lookup/all-attributes) supports per-item requestId values, enabling you to correlate individual items in batch requests back to your source records.

Two Types of Request IDs

Type

Location

Purpose

Format

API Call ID

results.meta.requestId

Identifies the entire API call for support/debugging

ULID (26 chars)

Per-Item ID

property.meta.requestId

Correlates individual items in batch requests

Your value or auto-generated

How It Works

  • If you provide a requestId: Your value is echoed back unchanged in the response

  • If you don't provide one: A short ID is auto-generated

  • Use per-item IDs to match results back to your original input records

Property Lookup Example

Request:

{
  "requests": [
    {
      "address": {
        "street": "123 Main St",
        "city": "Phoenix",
        "state": "AZ",
        "zip": "85001"
      },
      "requestId": "my-property-001"
    },
    {
      "address": {
        "street": "456 Oak Ave",
        "city": "Tempe",
        "state": "AZ",
        "zip": "85281"
      },
      "requestId": "my-property-002"
    }
  ]
}

Response:

{
  "results": {
    "properties": [
      {
        "address": {...},
        "meta": {
          "requestId": "my-property-001"
        }
      },
      {
        "address": {...},
        "meta": {
          "requestId": "my-property-002"
        }
      }
    ],
    "meta": {
      "apiVersion": "6.71.0",
      "requestId": "01KEVP21JEPJ1XQTE1TJSN94FQ"
    }
  }
}

Use Cases

Batch Processing Correlation:

# Build batch request with your database IDs
records = [
    {"id": "DB-001", "address": "123 Main St, Phoenix, AZ"},
    {"id": "DB-002", "address": "456 Oak Ave, Tempe, AZ"},
]requests = [{
    "address": parse_address(r["address"]),
    "requestId": r["id"]  # Use your database ID
} for r in records]# Send batch request
response = api.property_lookup(requests=requests)# Match results back to your records
for prop in response["results"]["properties"]:
    db_id = prop["meta"]["requestId"]
    # Update your database record using the matched ID

Debugging Failed Items:

{
  "results": {
    "properties": [
      {"meta": {"requestId": "prop-001"}, "address": {...}},
      {"meta": {"requestId": "prop-002", "error": true}, "errorMessage": "Address not found"}
    ]
  }
}

Best Practices

  • Use meaningful IDs - Include identifiers from your system (database IDs, file row numbers) rather than random strings

  • Keep IDs unique per batch - Each item should have a unique requestId to avoid confusion

  • Log the API Call ID - Always log results.meta.requestId for support requests and debugging


Customer Impact

Before

  • No standard way to correlate async requests with webhook responses

  • Required custom tracking logic and database lookups

  • Skip Trace V3 used deprecated inputId/inputIndex fields

  • Request IDs used inconsistent formats (ShortId, NanoId)

After

  • Immediate requestId returned for all async requests

  • Direct correlation between request and webhook using requestId

  • Self-contained webhook responses include original request context

  • Consistent ULID format across all endpoints

  • Skip Trace V3 uses standard requestId pattern


Backward Compatibility

  • Existing integrations continue to work without changes

  • The requestId in async responses is a new field (additive change)

  • Skip Trace V3 field removal is a breaking change for customers using inputId/inputIndex

  • Webhook payloads now include additional context fields (additive change)


Frequently Asked Questions

Q: Do I need to change my existing integrations?
A: Only if you use Skip Trace V3 and rely on inputId or inputIndex fields. All other integrations continue to work unchanged.

Q: Can I provide my own requestId for async requests?
A: The HTTP-level requestId is always system-generated. For batch endpoints that support per-item requestId, you can provide your own value which will be echoed back.

Q: What format should custom per-item requestId values use?
A: You can use any string value. Common choices include database IDs, UUIDs, or your own identifiers.

Q: Is the requestId guaranteed to be unique?
A: Yes. System-generated ULIDs are guaranteed unique. For custom per-item IDs, uniqueness is your responsibility.

Q: How long should I retain requestId mappings?
A: We recommend retaining mappings until you receive the webhook callback, plus a buffer period for retries. Webhook callbacks typically arrive within minutes but may take longer for large result sets.

Q: What if my webhook doesn't receive a callback?
A: Contact support with your requestId for troubleshooting. The ULID format includes a timestamp component that helps us locate your request in our logs.


Support

If you have questions about Request ID correlation or need assistance with your integration:


Technical Notes

ULID Specification

ULIDs follow the ULID specification:

 01AN4Z07BY      79KA1307SR9X4MV3
|----------|    |----------------|
 Timestamp          Randomness
   48bits             80bits

  • Timestamp: Millisecond precision, encoded in first 10 characters

  • Randomness: 80 bits of randomness for uniqueness within the same millisecond

  • Encoding: Crockford Base32 (excludes I, L, O, U to avoid confusion)

Async Request Flow

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Your Client   β”‚     β”‚   BatchData API   β”‚     β”‚  Your Webhook   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚                        β”‚                        β”‚
         β”‚  POST /async           β”‚                        β”‚
         β”‚  {webhookUrl: "..."}   β”‚                        β”‚
         │───────────────────────>β”‚                        β”‚
         β”‚                        β”‚                        β”‚
         β”‚  200 OK                β”‚                        β”‚
         β”‚  {requestId: "01KEV.."β”‚                        β”‚
         β”‚<───────────────────────│                        β”‚
         β”‚                        β”‚                        β”‚
         β”‚  Store requestId       β”‚  Process request       β”‚
         β”‚  with your job ID      β”‚  (async)               β”‚
         β”‚                        β”‚                        β”‚
         β”‚                        β”‚  POST webhook          β”‚
         β”‚                        β”‚  {meta: {requestId:    β”‚
         β”‚                        β”‚   "01KEV.."}, ...}     β”‚
         β”‚                        │───────────────────────>β”‚
         β”‚                        β”‚                        β”‚
         β”‚                        β”‚                        β”‚  Match requestId
         β”‚                        β”‚                        β”‚  to original job
         β”‚                        β”‚                        β”‚
         β”‚                        β”‚              200 OK    β”‚
         β”‚                        β”‚<───────────────────────│
         β”‚                        β”‚                        β”‚


Last Updated: January 2026

Did this answer your question?