If you don’t need realtime enrichment or are providing historical transaction data when first integrating with Spade you can use our batch endpoints instead of sending transactions individually. Batch enrichment allows you to process up to 50,000 transactions in a single request and retrieve the enriched results once processing is complete. Below you’ll find the data types supported and their corresponding endpoints:

Card Enrichment

  • /cards/enrich
  • /cards/enrich/parse

Transfers Enrichment

/transfers/enrich

Aggregated Data Enrichment

Coming soon!

Getting Started

Let’s walk through the process of enriching a batch of transactions:

Preparing your batch

Depending on the type of data you’re sending different fields may be required but regardless of whether you’re sending card, transfer or aggregated data we recommend the following: Using unique Transaction IDs Ensure each transaction in your batch has a unique transactionId. Results are not guaranteed to be returned in the order in which they were submitted, so you can use the transactionId to correlate transactions in the results with the original requests. Duplicate IDs will cause the batch enrichment request to fail. Sending data in chronological order We recommend sending your oldest transactions first as this can have an impact on the insights we can provide on transaction data such as recurrence. Using Custom attributes If you have additional data elements that you would like to associate to any transactions, you can use the customAttributes object which will be passed back alongside enriched transaction data.

Sending your batch

Once you have prepared your batch(es) you can send up to 10 requests per second to our batch enrichment endpoints.
Uploading large batches can take a while, so we recommend increasing the timeout of your client. We support up to a 120 second timeout window.
Below we’ve provided an example request for reference.
curl --request POST \
--url https://east.sandbox.spade.com/batches/transactions/cards/enrich \
--header 'content-type: application/json' \
--header 'X-Api-Key: SPADE-API-KEY' \
--data '{
  "callbackUrl": "https://example.com/callback",
  "transactions": [
    {
      "transactionId": "53DCD6BD-6220-408C-8B49-7761C9D35FC3",
      "userId": "2AF17111-7270-498F-8731-C023B1A85A48",
      "merchantName": "Wal-Mart Super Center",
      "amount": "5.00",
      "location": {
        "city": "PORT ORANGE",
        "region": "12",
        "country": "US",
        "postalCode": "11111"
      },
      "acquirerId": "",
      "occurredAt": "2021-01-01",
      "categoryCode": "5488",
      "categoryType": "MCC",
      "currencyCode": "USD"
    },
    {
      "transactionId": "747DF4B4-4D30-4E44-B1C7-0C3CA99AD55D",
      "userId": "7BDCC2E9-0E87-435E-866D-54C2552282C0",
      "merchantName": "Amazon",
      "acquirerId": "000000000123456",
      "amount": "25.23",
      "currencyCode": "USD",
      "occurredAt": "2022-06-15 18:27:51Z",
      "categoryCode": "5812",
      "categoryType": "MCC",
      "location": {
        "address": "1234 W 5th Ave Suite 100",
        "city": "New York",
        "region": "NY",
        "country": "USA",
        "postalCode": "10001",
        "latitude": 45,
        "longitude": 120
      }
    }
  ]
}'
Once successfully submitted we will send back a response object that includes the batchId which you’ll use later to retrieve the enriched transactions.
Batch response
{
  "batchId": "8590f0f5-2a4b-431e-baa8-cd3bfff22020",
  "status": "pending",
  "batchSize": 2,
  "submittedAt": "2025-06-30T21:46:10.827344Z"
}
You must use the batchId to retreive enriched data, so make sure you are storing it!

Handling failed batches

If your submitted batch fails we will return a 400 response with error details. Below is an example of an error response with duplicate transactionIds:
{
  "transactions": [
    "Transaction at index 1 has a duplicate ID."
  ]
}
Using the error details, make the changes required and re-submit the batch.

Checking batch status

A batch job can be in one of four states: * pending: The batch has been accepted but processing hasn’t started * running: The batch is currently being processed * completed: All transactions have been enriched and results are ready * failed: The batch encountered an error and couldn’t be processed We provide options for updates on the batch status either via webhooks or polling the status endpoint. We strongly recommend using webhooks as this reduces integration complexity as well as unnecessary API calls. Using webhooks If you provided a callbackUrl in your batch submission, we’ll send a POST request to that URL when processing completes. The callback will include a JSON payload with: * batchId: The ID of the completed batch * status: Either “completed” or “failed” We will also include a token in the X-Webhook-Token header that you should compare against the callback token provided to you by your Spade representative. Contact your Spade representative if you do not have this token. Here is an example Flask endpoint you could use to receive callbacks.
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/batch-notifications', methods=['POST'])
def batch_notification():
    webhook_token = request.headers.get('X-Webhook-Token')
    expected_token = "your-callback-token-from-spade"  # Store this securely

    if not webhook_token or webhook_token != expected_token:
        raise InvalidTokenError()

    data = request.json
    batch_id = data["batchId"]
    status = data["status"]

    if status == "completed":
        fetch_batch_results(batch_id)
    elif status == "failed":
        log_batch_failure(batch_id)
Polling for status You can poll the status endpoint until the status changes to a completed status. We recommend implementing an exponential backoff strategy when polling for status.
def wait_for_batch_completion_with_backoff(batch_id, max_attempts=10, initial_delay=60):
    """Wait for batch completion with exponential backoff"""
    delay = initial_delay

    while True:
        response = requests.get(
            f"https://east.sandbox.spade.com/batches/{batch_id}",
            headers={"X-Api-Key": "<Your API Key Here>"}
        )
        status = response.json()["status"]

        if status == "completed":
            return True
        elif status == "failed":
            raise Exception("Batch processing failed")

        print(f"Sleeping for {delay} seconds before checking status again")
        time.sleep(delay)
        delay = max(delay * 2, 60)

    raise Exception("Timeout waiting for batch completion")
Note: If the batch results in a failed status you will need to re-send the batch for processing.

Retrieving enriched data

Once the batch status is completed you can retrieve the enriched data by calling the /results endpoint.
{
  "batchId": "8590f0f5-2a4b-431e-baa8-cd3bfff22020",
  "status": "completed"
}
The /batches/{batchId}/results endpoint will only return results when the batch status is completed. If you request results before completion, you’ll receive a 202 status code indicating that the results aren’t yet available.
The results endpoint will return the batchId in addition to a results array that includes all of the transactions sent in the batch. Each transaction object in the results array will contain:
  • Your originaltransactionId
  • Any customAtrributes provided in the transaction
  • Astatus code (either 200 or 400)
  • If successful, the enriched transaction data will include an enrichmentId
  • If unsuccessful an error object
Below is an example repsonse returned:
{
  "batchId": "8590f0f5-2a4b-431e-baa8-cd3bfff22020",
  "results": [
    {
      "statusCode": 200,
      "transactionInfo": {
        "type": "spending",
        "subType": null,
        "display": {
          "name": "Walmart",
          "categoryName": "Department Stores",
          "graphic": "https://static.v2.spadeapi.com/logos/d730906bf1a849f19939f27390170a6d/light.png",
          "graphicSource": "counterparty"
        },
        "thirdParties": [],
        "spendingInfo": {
          "channel": {
            "value": "physical"
          }
        },
        "transferInfo": null,
        "atmInfo": null,
        "isAccountVerification": null,
        "isPeerToPeer": null,
        "isDigitalWallet": null,
        "transactionId": "53DCD6BD-6220-408C-8B49-7761C9D35FC3",
        "recurrenceInfo": null,
        "riskInsights": {
          "irregularWebPresenceDetected": false,
          "negativeOnlineSentiment": false,
          "scammerFlag": false,
          "riskyIndustry": false,
          "locationMaturityLevel": "0.>=25yrs",
          "counterpartyMaturityLevel": "0.>=25yrs"
        }
      },
      "counterparty": [
        {
          "id": "d730906b-f1a8-49f1-9939-f27390170a6d",
          "name": "Walmart",
          "legalName": "Walmart Inc.",
          "industry": [
            {
              "id": "011-000-000-000",
              "name": "Retail",
              "icon": "https://static.v2.spadeapi.com/categories/ee4ee39fd5474d31ac42f9e606b9040a/light.png"
            },
            {
              "id": "011-018-000-000",
              "name": "General Goods",
              "icon": "https://static.v2.spadeapi.com/categories/ee4ee39fd5474d31ac42f9e606b9040a/light.png"
            },
            {
              "id": "011-018-002-000",
              "name": "Department Stores",
              "icon": "https://static.v2.spadeapi.com/categories/ee4ee39fd5474d31ac42f9e606b9040a/light.png"
            }
          ],
          "location": [
            {
              "id": "380e18b7-bf9e-3545-b27e-80e36301c540",
              "address": "1590 Dunlawton Ave",
              "addressLine1": "1590 Dunlawton Ave",
              "addressLine2": null,
              "city": "Port Orange",
              "region": "FL",
              "postalCode": "32127",
              "country": "USA",
              "phoneNumber": "+13867610191",
              "latitude": 29.116753,
              "longitude": -81.019165,
              "matchScore": 85.35
            }
          ],
          "matchScore": 89.62,
          "logo": "https://static.v2.spadeapi.com/logos/d730906bf1a849f19939f27390170a6d/light.png",
          "medianSpendPerTransaction": null,
          "phoneNumber": "+14792734000",
          "website": "https://www.walmart.com/"
        }
      ],
      "enrichmentId": "967c0acc-92a0-4212-bbed-3a7209b58bf6"
    },
    {
      "statusCode": 200,
      "transactionInfo": {
        "type": "spending",
        "subType": null,
        "display": {
          "name": "Amazon",
          "categoryName": "Online Marketplace",
          "graphic": "https://static.v2.spadeapi.com/logos/5f35110e8de74f9cadc6b28bf87dd5b6/light.png",
          "graphicSource": "counterparty"
        },
        "thirdParties": [],
        "spendingInfo": {
          "channel": {
            "value": "digital"
          }
        },
        "transferInfo": null,
        "atmInfo": null,
        "isAccountVerification": null,
        "isPeerToPeer": null,
        "isDigitalWallet": null,
        "transactionId": "747DF4B4-4D30-4E44-B1C7-0C3CA99AD55D",
        "recurrenceInfo": null,
        "riskInsights": {
          "irregularWebPresenceDetected": false,
          "negativeOnlineSentiment": false,
          "scammerFlag": false,
          "riskyIndustry": false,
          "locationMaturityLevel": "6.<=1yrs",
          "counterpartyMaturityLevel": "0.>=25yrs"
        }
      },
      "counterparty": [
        {
          "id": "5f35110e-8de7-4f9c-adc6-b28bf87dd5b6",
          "name": "Amazon",
          "legalName": "Amazon.com, Inc.",
          "industry": [
            {
              "id": "011-000-000-000",
              "name": "Retail",
              "icon": "https://static.v2.spadeapi.com/categories/ee4ee39fd5474d31ac42f9e606b9040a/light.png"
            },
            {
              "id": "011-010-000-000",
              "name": "Online Marketplace",
              "icon": "https://static.v2.spadeapi.com/categories/b4b0d249b40249acb7445027d4574fc5/light.png"
            }
          ],
          "location": [
            {
              "id": null,
              "address": "1234 W 5th Ave Suite 100",
              "addressLine1": "1234 W 5th Ave Suite 100",
              "addressLine2": null,
              "city": "New York",
              "region": "NY",
              "postalCode": "10001",
              "country": "USA",
              "phoneNumber": null,
              "latitude": 45,
              "longitude": 120,
              "matchScore": null
            }
          ],
          "matchScore": 98.26,
          "logo": "https://static.v2.spadeapi.com/logos/5f35110e8de74f9cadc6b28bf87dd5b6/light.png",
          "medianSpendPerTransaction": null,
          "phoneNumber": "+12062661000",
          "website": "https://www.amazon.com"
        }
      ]
      "enrichmentId": "aae9c6bc-1cea-4ca8-8f70-dc85e2d21224"
    }
  ]
}
response = requests.get(
    f"https://east.sandbox.spade.com/batches/{batch_id}/results",
    headers={"X-Api-Key": "<Your API Key Here>"}
)

results = response.json()["results"]

for enriched_transaction in results:
    print(f"Enriched transaction: {enriched_transaction['enrichmentId']}")
    # ... process the enrichment ...

Handling errors:

For transactions that resulted in errors we will provide an errors object that includes detailed information that caused the error. An example is included below for a request that did not include a merchantName
    {
      "statusCode": 400,
      "transactionInfo": {
        "transactionId": "53DCD6BD-6220-408C-8B49-7761C9D35FC3"
      },
      "errors": {
        "merchantName": [
          "This field is required."
        ]
      }
    }
See our enrichment guide for more details on handling errors from our API.