Skip to content
LeadityDocs

Spending credits

Idempotency

Every call that takes credits carries a key of yours, so a retry replays the first answer instead of buying twice.

A network can drop the answer to a request that succeeded. Without protection, the natural retry would place a second order and take the credits twice. Every operation that takes credits therefore requires an Idempotency-Key header.

curl -X POST "https://api.leadity.io/v1/scrapes" \
  -H "Authorization: Bearer $LEADITY_API_KEY" \
  -H "Idempotency-Key: tx-roofers-2026-10-04" \
  -H "Content-Type: application/json" \
  -d @order.json

The rules

  • One key per purchase. Make a new key for each order you mean to place, and keep it with that order.
  • The same key for every retry. Sending the same key with the same request again replays the first answer: nothing is charged a second time, and the answer says so with the header Idempotent-Replayed: true.
  • A key is tied to its request. The same key with a different body is refused with 409 idempotency_conflict, so a key can never buy something you did not ask for. Do not retry a conflict: send the new request under a new key.
  • A refusal leaves the key free. A call refused for any reason, such as insufficient_credits or quote_exceeded, took nothing, so fix the cause and send it again under the same key, changed or not.
  • The shape is fixed. 16 to 64 characters of letters, digits, _ and -. A UUID works. A key outside that shape is refused with invalid_idempotency_key rather than ignored, because believing you are protected when you are not is the state the key exists to prevent.
  • A spend without one is refused with idempotency_key_required. Reads never need a key.

What a retry answers

The first requestA retry with the same key and request
Placed a scrape or created a validation run200 with that scrape or run, "replayed": true in the body and Idempotent-Replayed: true
Bought an upgrade to a finished scrapeThe first answer again, verbatim, with Idempotent-Replayed: true
Is still being processed503 try_again with Retry-After: 2: wait that long and retry with the same key, which then reads the first answer
Was refusedRuns again, because the refusal took nothing

A key names its request for 24 hours. After that, a retry of an order or a list still answers with the scrape or run its key made, and a retried upgrade meets the upgrade's own state, such as already_purchased, rather than buying it twice.

Python
import os
import uuid

import requests

# Make the key once, before the first attempt.
key = str(uuid.uuid4())

for attempt in range(3):
    try:
        response = requests.post(
            "https://api.leadity.io/v1/scrapes",
            headers={
                "Authorization": f"Bearer {os.environ['LEADITY_API_KEY']}",
                "Idempotency-Key": key,
            },
            json=order,
            timeout=30,
        )
        break
    except requests.ConnectionError:
        continue  # Same key on every attempt: at most one order is placed.

On the MCP server

An MCP tool takes no idempotency key: the server makes one from the call itself. The same MCP key and the same tool with the same arguments inside the same ten-minute window give the same idempotency key, so a model that retries a spend replays the first answer. A deliberately identical second order inside that window also replays, so change something, such as the title or the lead count, to buy again.

The windows are fixed slots of the clock, not ten minutes from each call, so a retry that falls just past a slot's end makes a new key and is a new purchase. Its maxCredits still caps what it can take, and a second upgrade on the same scrape is refused by the scrape's own state.