How to spot duplicate AI API charges (and stop paying twice)

By NeverPayTwice · November 14, 2026 · 6-minute read

Short answer: AI APIs bill every request they process, including one you already paid for a minute ago. Retry loops, double-clicks, page refreshes and re-run pipelines all buy the same answer again.

To find them, give each request a fingerprint of its body (ignoring fields that only identify the caller), group requests with the same fingerprint, and add up what the extra copies cost. A tested script is below.

The kinds of double payment

1. The same request, sent again

The most common one, and the easiest to miss, because every copy succeeds and looks normal on its own. Typical causes: a user presses the button twice, a page refresh re-sends the request, a scheduled job or agent step runs again over the same input, or a cache that should have answered from memory didn't.

2. Retries that were processed twice

A request times out on your side, your code retries, but the first attempt had already been processed. A retry is a new request, so you can pay for both. Short client timeouts on long answers make this more likely.

3. The same charge billed twice

Rare, but worth checking: the same request ID or charge ID appearing twice in a usage export or invoice. This is a billing error, and the request ID is your evidence.

4. Requests that failed but used tokens

Some failed or interrupted requests still report token usage. Whether, and how, you are charged for them depends on the provider and the kind of failure, so check your own records: a response that carries an error and token usage is worth a look.

How to find them

  1. Keep a record of each request: the time, the endpoint, the request body, the request ID the provider returned, and the cost worked out from its usage (see how to work out the cost of each request).
  2. Fingerprint the request body. Two requests for the same information have the same model, messages and settings. Fields like user or metadata identify who asked, not what was asked, so leave them out, and sort the keys so their order doesn't matter.
  3. Group by fingerprint. Every group with more than one request is something you paid for more than once. The time between copies tells you the likely cause: seconds apart usually means a double-click or a retry; hours apart usually means the same question asked again.
  4. Check exports for repeated request IDs. The same request ID twice in a bill is the billing error in point 3.

A tested Python script

Feed it the calls you recorded. It returns each request you paid for more than once, how many times, what the extra copies cost and how far apart they were. (Download it, or get it with the other scripts on GitHub.)

import hashlib
import json

IGNORE = {"metadata", "user", "stream_options", "service_tier"}


def fingerprint(endpoint, body):
    """Same endpoint + same request body (ignoring caller-only fields) -> same fingerprint."""
    if isinstance(body, dict):
        body = {k: v for k, v in body.items() if k not in IGNORE}
    canon = json.dumps(body, sort_keys=True, separators=(",", ":"), ensure_ascii=False)
    return hashlib.sha256((endpoint + "\n" + canon).encode()).hexdigest()


def find_repeats(calls):
    """calls: list of dicts with "time", "endpoint", "body" and "cost" (dollars).
    Returns one entry per request that was paid for more than once, most expensive repeats first."""
    groups = {}
    for c in sorted(calls, key=lambda c: c["time"]):
        groups.setdefault(fingerprint(c["endpoint"], c["body"]), []).append(c)
    repeats = []
    for calls_ in groups.values():
        if len(calls_) > 1:
            repeats.append({
                "first": calls_[0]["time"],
                "times_paid": len(calls_),
                "repeat_cost": sum(c["cost"] or 0 for c in calls_[1:]),   # everything after the first purchase
                "seconds_apart": [round(b["time"] - a["time"], 1) for a, b in zip(calls_, calls_[1:])],
            })
    return sorted(repeats, key=lambda r: -r["repeat_cost"])

Example: the same question sent at 0 s, again 2.5 s later (a double-click, with a different user field) and again 15 minutes later gives {'times_paid': 3, 'repeat_cost': 0.023, 'seconds_apart': [2.5, 897.5]}. A different question is not counted. We checked the fingerprint against the one NeverPayTwice's live meter uses: they agree on all 36 pairs we tested.

This finds exact repeats. A question asked with slightly different wording is a new request with a new fingerprint; whether it was "the same question" is a judgement call, so review those by hand.

What to do about each kind

Or let something keep watch

NeverPayTwice is a small app we make. Its live meter runs on your own computer between your code and the API (Anthropic, OpenAI, Google Gemini and compatible APIs). It never blocks or changes a request. It records each one and flags every repeat charge as a double spend linked to the original purchase receipt, with a refund ledger and a printable statement. Nothing is uploaded and API keys are never stored. Mac, Windows or Linux, $9 one time.

See double-spend detection and receipts with sample data first, no sign-up.

Try the demoGet NeverPayTwice · $9