Idempotency

Make Ur Next Door API retries safe with the Idempotency-Key header: key format, 24 hour replay, conflicts, and code to generate keys in four languages.

View as Markdown

If a request times out, you cannot know whether it worked. Sending it again might create a second post. The Idempotency-Key header solves this: send a unique key with each new request, and reuse it only when you retry that same request. The API then acts once, and answers every retry with the result of the first attempt.

How it works

  1. You send POST /api/v1/posts with Idempotency-Key: 6f1c2b8e-....
  2. The post is created and the API stores the 2xx answer for 24 hours, linked to that key.
  3. You send the identical request again with the same key, within 24 hours. Nothing new is created. You get the stored answer, with the header Idempotent-Replayed: true.

Only successful (2xx) answers are stored. If the first attempt failed (a 422, a 500, anything not 2xx), a retry with the same key runs the request again, so you can fix a problem and resend with the same key.

The rules

RuleDetail
WhereAny POST or DELETE. Ignored on GET.
Format1 to 64 characters: letters, digits, ., _, : or -. A UUID is ideal. Anything else answers 400 invalid_idempotency_key.
LifetimeA stored answer replays for 24 hours.
ScopeKeys are remembered per API key.
Same key, different request422 idempotency_key_reused. A key belongs to one exact request: the same method, path and body.
Same key while the first is running409 idempotency_in_progress. Wait a moment and retry with the same key.

Generate a key

cURL
IDEMPOTENCY_KEY=$(uuidgen)
JavaScript
const idempotencyKey = crypto.randomUUID();
Python
import uuid

idempotency_key = str(uuid.uuid4())
PHP
<?php
$idempotencyKey = bin2hex(random_bytes(16));

Retry the right way

Create the key once per item, before the first attempt, and keep it for every retry of that item:

Python
import os
import time
import uuid

import requests

api_key = os.environ["UND_API_KEY"]
payload = {"title": "Five easy ways to save water at home", "body": "<p>Small habits add up.</p>"}
key = str(uuid.uuid4())  # one key for this post, reused on every retry

for attempt in range(3):
    try:
        r = requests.post(
            "https://urnextdoor.com/api/v1/posts",
            headers={"Authorization": f"Bearer {api_key}", "Idempotency-Key": key},
            json=payload,
            timeout=30,
        )
    except requests.RequestException:
        time.sleep(2 ** attempt)
        continue
    if r.status_code >= 500 or r.status_code == 409:
        time.sleep(2 ** attempt)
        continue
    print(r.status_code, r.json())
    break

When you create many items in a loop, make a new key for each item. Reusing one key for different posts answers 422 idempotency_key_reused.

Good keys from your own data

If your items already have a stable id (a spreadsheet row id, a database id), you can build the key from it, for example sheet-row-1042. Then even a workflow that runs twice by mistake within 24 hours cannot publish that row twice.

Next steps

Last updated