APIFuseAPIFuseDocs

Getting started

  • Overview
  • Quickstart

Authentication

  • Managed keys
  • Scopes

Errors

  • Error model
  • Retries

Webhooks

  • Overview
  • Signatures

API reference

  • Amazon Japan
  • Baemin Provider
  • Buyee
  • CatchTable Restaurant Search and Reservations
  • Charan Commerce
  • Daangn public listings
  • Daiso Product and Store Data
  • Danawa price comparison
  • Demaecan
  • Ekitan
  • Goodchoice Domestic Lodging
  • Google Flights
  • Han River Water Level
  • Hot Pepper Gourmet provider
  • Hyundai Card
  • Jalan
  • Japan National Diet Minutes
  • Japan Disaster Alerts
  • Japan EDINET Filings
  • Japan e-Gov Law
  • Japan e-Stat
  • Japan GSI Geocoding
  • Japan e-Gov Open Data
  • Japan Post ZIP
  • Japan Public Holidays
  • JMA Weather
  • KakaoMap Place Search and Directions
  • Kakao T Taxi Dispatch
  • Korea Address Search
  • AirKorea Real-time Air Pollution
  • Korea Apartment Rent Prices
  • Korea Apartment Sale Prices
  • Korea Bid Notices
  • Korea Building Register
  • Korea Business Verify
  • Korea Camping
  • Korea DART Corporate Finance
  • DART Corporate Info
  • Korea Culture Events
  • Korea Disaster Alert
  • Korea Emergency Hospital
  • Korea ETF
  • Korea EV Charger
  • Korea Fuel Price
  • Korea Holiday
  • Korea Hospital Info
  • Korea Land Price
  • Korea MFDS Drug Safety
  • Korea MFDS Food Safety
  • Korea Household Waste Disposal Guide
  • Korea National Law Search and Lookup
  • Korea NEIS School Meals
  • Carrier List and Delivery Tracking
  • Korea Pharmacy
  • Korea Population
  • Korea Stock Index
  • Korea Stock Price
  • Korea Train Schedule
  • Korea Weather Forecast Data
  • Korea Weather Forecast
  • Korea Welfare Service
  • K-Startup
  • LH Housing Notices
  • Market Kurly product data
  • Mercari
  • Modu Parking
  • Naver Blog Search
  • Naver Flight API
  • Naver Map
  • Naver News Search
  • NOL Stays
  • Ohouse Store and Contents
  • Rakuten Ichiba
  • Rakuten Travel
  • SEC EDGAR Filings
  • Seoul Bike
  • Seoul Live Crowd Density
  • Seoul Subway Arrivals
  • Shinhan Bank
  • Shinhan Card
  • Skiplagged
  • SUUMO
  • Swing Taxi
  • Tabelog
  • TableCheck
  • Weverse Provider
  • Yahoo! Shopping (Japan)
  • Yogiyo
  • ZOZOTOWN

Guides

  • APIFuse Docs
  • Getting started
  • Authentication and Connections
  • Playground
  • OpenAPI and schemas
  • MCP endpoint
  • Developer MCP guide
  • Schema bundles
  • Next.js App Router integration
  • FastAPI integration
  • Error Handling
  • FAQ
  • Resources

Changelog

  • Changelog
APIFuseAPIFuse
DocsServicesPlaygroundStatus
Log inSign up

Errors

Retries

Retry rules and backoff behaviour for idempotent calls.

Blindly resending a failed call can create a reservation twice or dig a rate limit deeper. Retrying comes down to three questions: is this call idempotent, how long should you wait before the next attempt, and is this error the kind a retry can fix. The envelope from the error model answers the last one directly.

What is safe to retry

GET and DELETE are idempotent. Sending the same request again does not change the outcome, so after a network error or a 502 you can retry them freely. POST is not idempotent by default: resending a Create reservation call that never got an answer can book the table twice.

MethodIdempotentRetry
GETYesSafe. Retry with backoff and jitter.
DELETEYesSafe. Repeating the same cancellation does not change the outcome.
POSTNoState creating POSTs are safe only with an Idempotency-Key header. Read only POSTs such as Estimate ride say so in their reference description and are safe as is.

To make a POST retryable, send an Idempotency-Key header on the first attempt. The gateway remembers the key for 24 hours and answers any repeat of it with the result of the first processing. Every state creating POST accepts the header, including Create reservation and Join waitlist.

POST with an Idempotency-Key
curl -X POST "https://api.frism.dev/v1/catch-table/reservations" \
  -H "Authorization: Bearer frism_test_k3xample" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: idem_b1f7c2d0" \
  -d '{
    "restaurant_id": "res_01J8Q9W3TE",
    "slot_id": "slt_1900_2",
    "party_size": 2,
    "contact_name": "Han Jiwoo"
  }'
An Idempotency-Key expires after 24 hours. A request arriving with the same key after that is treated as new.

Backoff and jitter

Grow the wait between attempts exponentially and add random jitter to each one. When every client retries on a fixed schedule, a service recovering from an outage gets hit by all of them at once, which slows the recovery down. There is one exception: a 429 carries a Retry-After header, and when it is present you follow the header instead of your computed backoff.

Cap the number of attempts. A request that has not succeeded after three or four retries should return its error rather than sit in a queue, letting the caller inform the user or try again later. A client that retries without a ceiling only prolongs the outage.

The retryable flag

The retryable field in the envelope is the gateway's own verdict on whether a retry can succeed. It is true on 429 rate_limited and 502 provider_unavailable. Branching on this flag instead of hardcoding a status list means your client keeps working if the mapping ever grows.

A retry helper

The helper below implements all of the rules above. It only retries network errors, 429 and 502, prefers Retry-After when the response carries one, and falls back to full jitter backoff otherwise.

Retrying fetch wrapper
const BASE_URL = "https://api.frism.dev/v1";
const RETRYABLE_STATUSES = new Set([429, 502]);

interface FrismRequest {
  method?: "GET" | "POST" | "DELETE";
  headers?: Record<string, string>;
  body?: string;
}

function sleep(ms: number): Promise<void> {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

// Full jitter: a random wait between 0 and the exponential ceiling.
function backoffMs(attempt: number, baseMs = 500, capMs = 8_000): number {
  const ceiling = Math.min(capMs, baseMs * 2 ** (attempt - 1));
  return Math.random() * ceiling;
}

export async  frismFetch(
  path: string,
  init: FrismRequest = {},
  maxAttempts = ,
): Promise<Response> {
  for ( attempt = ; ; attempt += ) {
     response: Response | undefined;
     {
      response =  fetch(`${BASE_URL}${path}`, {
        ...init,
        headers: {
          Authorization: ,
          ...init.headers,
        },
      });
    }  (cause) {
      
       (attempt >= maxAttempts)  cause;
    }

     (response) {
      
       (!RETRYABLE_STATUSES.has(response.status))  response;
       (attempt >= maxAttempts)  response;
    }

    
     retryAfter = Number(response?.headers.get());
     delayMs = retryAfter >  ? retryAfter *  : backoffMs(attempt);
     sleep(delayMs);
  }
}

When a POST goes through it, pass the Idempotency-Key along.

Creating a reservation through the helper
const response = await frismFetch("/catch-table/reservations", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": "idem_b1f7c2d0",
  },
  body: JSON.stringify({
    restaurant_id: "res_01J8Q9W3TE",
    slot_id: "slt_1900_2",
    party_size: 2,
    contact_name: "Han Jiwoo",
  }),
});

if (!response.ok) {
  // Reaching here means no retry can fix it. Branch on the code.
  const { error } = (await response.json()) as { error: { code: string } };
  console.error(error.code);
}

What not to retry

  • 409 conflict: the resource moved on. Do not repeat the same request; re-read and build a new request with another slot.
  • 422 provider_rejected: the service's policy refused the request. Sending it again returns the same answer every time.
  • 400, 401, 403, 404: the request itself is wrong. There is nothing to retry until you fix it.

What each status and code means is laid out in the . And the fewer things you poll, the fewer retries you need: state changes arrive as pushes, see the .

function
4
let
1
1
let
try
await
"Bearer frism_test_k3xample"
catch
// Network errors retry like a 502.
if
throw
if
// Success, and errors a retry cannot fix, return as-is.
if
return
if
return
// A Retry-After from a 429 wins over the computed backoff.
const
"Retry-After"
const
0
1000
await
availability
error model
webhooks guide