{
  "openapi": "3.1.0",
  "info": {
    "title": "RecallDB API",
    "version": "1.0.0",
    "description": "Recall data API for vehicles, motorcycles, RVs, car seats, tires, consumer products and food, from NHTSA, CPSC, FDA, USDA FSIS and Canadian and UK agencies. Free (beta) keys: 1,000 requests per month and 10 per minute, for non-commercial use. One key per verified email. A 429 answer says which limit was reached and when to try again. Attribution is required (\"Data from RecallDB\" with a link to https://recalldb.app). There is no bulk export. Always confirm a recall with the manufacturer or the agency.",
    "contact": {
      "name": "RecallDB support",
      "email": "support@recalldb.app",
      "url": "https://recalldb.app"
    },
    "license": {
      "name": "Recall data is U.S. government public-domain data; attribution to RecallDB is required for free keys",
      "url": "https://recalldb.app/docs/attribution"
    }
  },
  "servers": [
    {
      "url": "https://recalldb.app"
    }
  ],
  "externalDocs": {
    "description": "Documentation",
    "url": "https://recalldb.app/docs"
  },
  "tags": [
    {
      "name": "Recalls"
    },
    {
      "name": "Webhooks"
    },
    {
      "name": "Status"
    }
  ],
  "paths": {
    "/v1/recalls/{id}": {
      "get": {
        "operationId": "getRecall",
        "summary": "Get a recall",
        "description": "Returns one recall with the agency's own text, a plain-English summary, and where the data came from. Use the NHTSA campaign number for vehicles and car equipment (21V215000), cpsc- plus the CPSC recall number for consumer products (cpsc-27064), and fda- plus the FDA event number for food (fda-98506).\n\ndistribution says where the notice says it was sold: type is nationwide, states (with state codes) or not_specified, and as_listed is the notice's own wording. It is null for recalls with no distribution (vehicles, tires and others). A state list is only what the notice names: never a statement that an item was or was not sold to you.\n\nThe id in the response is exactly the id in the URL: NHTSA numbers in capitals (21V215000), cpsc-<number> and fda-<event number> in lower case. source.official_id is the agency's own number (for CPSC and FDA, without the prefix).\n\nunits is the agency's wording (for example \"About 265,000\"). units_affected is the same count as a whole number, or null when the wording is not a clear single count (ranges, amounts in cases or pounds, no number). It is never a guess.\n\nLot, serial and date codes appear only as codes_as_listed, copied exactly as the agency wrote them. RecallDB never compares them with anything and never says whether a particular item is affected.\n\nRecalls are added as they are published: NHTSA, CPSC and FDA food updates run every day.",
        "tags": [
          "Recalls"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "NHTSA campaign number, cpsc-<number> or fda-<event number>. Not case sensitive.",
            "schema": {
              "type": "string"
            },
            "example": "21V215000"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "id": "21V215000",
                    "category": "vehicle",
                    "issued_by": "Honda",
                    "issuer_note": null,
                    "severity": "standard",
                    "date": "2021-03-25",
                    "units": 624552,
                    "units_affected": 624552,
                    "official_text": {
                      "problem": "Honda is recalling certain 2018-2020 vehicles. The low-pressure fuel pump inside the fuel tank may fail.",
                      "consequence": "Fuel pump failure can cause an engine stall while driving, increasing the risk of a crash.",
                      "remedy": "Dealers will replace the fuel pump assembly, free of charge.",
                      "notes": ""
                    },
                    "components": [
                      "FUEL SYSTEM, GASOLINE:DELIVERY:FUEL PUMP"
                    ],
                    "codes_as_listed": [],
                    "plain_summary": {
                      "whatsWrong": "The low-pressure fuel pump inside the fuel tank may fail.",
                      "danger": "Fuel pump failure can cause an engine stall while driving, increasing the risk of a crash.",
                      "whatToDo": "Dealers will replace the fuel pump assembly, free of charge.",
                      "advisory": ""
                    },
                    "distribution": null,
                    "page_url": "https://checkrecalls.org/recall/21v215000"
                  },
                  "source": {
                    "agency": "NHTSA",
                    "official_id": "21V215000",
                    "official_url": "https://www.nhtsa.gov/recalls?nhtsaId=21V215000",
                    "last_updated": "2026-11-13"
                  },
                  "attribution": {
                    "text": "Data from RecallDB",
                    "url": "https://recalldb.app"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key suspended, or a feature not in your plan (plan_limit)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit (rate_limited) or monthly quota (quota_exceeded) reached. The message says which and when to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/v1/vehicles/{make}/{model}/{year}": {
      "get": {
        "operationId": "getVehicleRecalls",
        "summary": "Recalls for a vehicle",
        "description": "Returns the recalls NHTSA lists for a model and model year, newest first, each with its own source block. Coverage: 89 makes of passenger cars, SUVs, minivans and pickups, all model years.  Recalls are listed by model and year, so not every vehicle of that model is part of each recall: use the VIN endpoint plus NHTSA or the carmaker to check a specific car.\n\nMake and model names follow checkrecalls.org addresses, so /cars/honda/civic/2019 there is /v1/vehicles/honda/civic/2019 here.\n\nA model-year page that is not published yet answers 404; check again later.",
        "tags": [
          "Recalls"
        ],
        "parameters": [
          {
            "name": "make",
            "in": "path",
            "required": true,
            "description": "Lowercase make name as on checkrecalls.org (honda, mercedes-benz, land-rover).",
            "schema": {
              "type": "string"
            },
            "example": "honda"
          },
          {
            "name": "model",
            "in": "path",
            "required": true,
            "description": "Lowercase model name (civic, f-150, cx-5).",
            "schema": {
              "type": "string"
            },
            "example": "civic"
          },
          {
            "name": "year",
            "in": "path",
            "required": true,
            "description": "Model year, any year NHTSA lists (for example 2019).",
            "schema": {
              "type": "string"
            },
            "example": "2019"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "vehicle": {
                      "make": "Honda",
                      "model": "Civic",
                      "year": 2019
                    },
                    "recall_count": 4,
                    "recalls": [
                      {
                        "id": "21V215000",
                        "title": "Recall 21V215000",
                        "date": "2021-03-25",
                        "component": "FUEL SYSTEM, GASOLINE:DELIVERY:FUEL PUMP",
                        "issued_by": "Honda",
                        "issuer_note": null,
                        "severity": "standard",
                        "plain_summary": {
                          "whatsWrong": "…",
                          "danger": "…",
                          "whatToDo": "…",
                          "advisory": ""
                        },
                        "page_url": "https://checkrecalls.org/recall/21v215000",
                        "source": {
                          "agency": "NHTSA",
                          "official_id": "21V215000",
                          "official_url": "https://www.nhtsa.gov/recalls?nhtsaId=21V215000",
                          "last_updated": "2026-11-13"
                        }
                      }
                    ],
                    "truncated": false,
                    "note": "Recalls are listed by model and year. Not every vehicle of this model and year is part of each recall."
                  },
                  "source": {
                    "agency": "NHTSA",
                    "official_id": null,
                    "official_url": "https://www.nhtsa.gov/recalls",
                    "last_updated": "2026-11-13"
                  },
                  "attribution": {
                    "text": "Data from RecallDB",
                    "url": "https://recalldb.app"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key suspended, or a feature not in your plan (plan_limit)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit (rate_limited) or monthly quota (quota_exceeded) reached. The message says which and when to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/v1/recalls/{id}/history": {
      "get": {
        "operationId": "getRecallHistory",
        "summary": "Recall change history",
        "description": "Agencies correct recalls: they widen the models, fix the remedy or change the number of units. We keep each change to an official field (problem, consequence, remedy, notes, units, issued by, date, components) as the field, the old value, the new value and the day we recorded it.\n\nThe free tier returns the latest recorded change only. The full history of every change is a planned Pro feature (see pricing). A recall that has never changed returns an empty list.\n\nCounts as one request. History starts on the day we began recording it (2026-11-13); changes before that are not known.",
        "tags": [
          "Recalls"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The recall id, as in /v1/recalls/{id}.",
            "schema": {
              "type": "string"
            },
            "example": "21V215000"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "id": "21V215000",
                    "changes": [
                      {
                        "field": "units",
                        "old": "600000",
                        "new": "624552",
                        "changed_on": "2026-11-14"
                      }
                    ],
                    "history_limit": "latest change only on the free tier; the full history is a planned Pro feature",
                    "dates_note": "changed_on is the U.S. Eastern day we recorded the change."
                  },
                  "source": {
                    "agency": "NHTSA",
                    "official_id": "21V215000",
                    "official_url": "https://www.nhtsa.gov/recalls?nhtsaId=21V215000",
                    "last_updated": "2026-11-13"
                  },
                  "attribution": {
                    "text": "Data from RecallDB",
                    "url": "https://recalldb.app"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key suspended, or a feature not in your plan (plan_limit)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit (rate_limited) or monthly quota (quota_exceeded) reached. The message says which and when to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/v1/search": {
      "get": {
        "operationId": "searchRecalls",
        "summary": "Search recalls",
        "description": "Searches recall records with the same full-text index as checkrecalls.org. It matches product names, brands, model numbers, recall numbers and UPCs across vehicles, motorcycles, RVs, car seats, tires, car equipment, consumer products, food, dietary supplements, drugs and medical devices. Each result has the same id you would use in /v1/recalls/{id}, and a source block with the agency, its own number and a link to the official notice.\n\nA search shows what the official notices mention. A search with no results never means a product has no recall.\n\nEach search counts as one request against your quota and is checked by the same fair-use rules as every other endpoint. Paging is capped at 20 pages of 50 (1,000 matches): narrow the search instead of paging further. meta.total is the real number of matches up to 10,000; when there are more, meta.total is 10000 and meta.capped is true (read it as \"10,000 or more\"). Only the first 1,000 matches can be paged, whatever the total.\n\nDates in the results are the agencies' own dates (ISO, no time zone). Other dates in the API are ISO dates in UTC; the checkrecalls.org pages show \"last updated\" in U.S. Eastern time.\n\nOnly published records are searched. Bulk collection by searching term after term is not allowed (see acceptable use).",
        "tags": [
          "Recalls"
        ],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "What to search for: at least 2 letters or numbers, at most 100 characters. Letters and numbers only are used.",
            "schema": {
              "type": "string"
            },
            "example": "bunk bed"
          },
          {
            "name": "category",
            "in": "query",
            "required": false,
            "description": "vehicle, motorcycle, rv, car_seat, tire, equipment, consumer_product, food, dietary_supplement, drug, medical_device, ca_vehicle, ca_product, ca_food, uk_food, au_product, eu_product, uk_product, uk_medicine, meat_poultry or public_health_alert.",
            "schema": {
              "type": "string"
            },
            "example": "consumer_product"
          },
          {
            "name": "agency",
            "in": "query",
            "required": false,
            "description": "nhtsa, cpsc, fda, usda, tc, hc, cfia or fsa.",
            "schema": {
              "type": "string"
            },
            "example": "cpsc"
          },
          {
            "name": "fuzzy",
            "in": "query",
            "required": false,
            "description": "true (default) or false. When nothing matches, names (makes, models, brands, companies, product and food names) are tried with a corrected spelling, for example toyta becomes toyota, and meta.corrected_query says what was searched. Recall numbers, UPCs, model numbers, VINs and anything with a digit are always exact. fuzzy=false turns the correction off.",
            "schema": {
              "type": "string"
            },
            "example": "false"
          },
          {
            "name": "state",
            "in": "query",
            "required": false,
            "description": "A U.S. state code such as TX. Keeps recalls sold nationwide, in that state, or with no clear distribution. A recall is only dropped when its notice clearly lists other states and not this one.",
            "schema": {
              "type": "string"
            },
            "example": "TX"
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Only recalls dated on or after this day (YYYY-MM-DD, as the agency dates them).",
            "schema": {
              "type": "string"
            },
            "example": "2026-01-01"
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Only recalls dated on or before this day.",
            "schema": {
              "type": "string"
            },
            "example": "2026-06-30"
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page number, 1 to 20 (default 1).",
            "schema": {
              "type": "string"
            },
            "example": "1"
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "description": "Results per page, 1 to 50 (default 20).",
            "schema": {
              "type": "string"
            },
            "example": "20"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "id": "cpsc-27064",
                      "title": "Arlopu Slide Playset Recall: CPSC Recall 27064",
                      "category": "consumer_product",
                      "issued_by": "Arlopu",
                      "date": "2026-10-29",
                      "units": "About 4,000",
                      "units_affected": 4000,
                      "distribution": {
                        "type": "nationwide",
                        "states": [],
                        "as_listed": null
                      },
                      "page_url": "https://checkrecalls.org/recall/cpsc-27064",
                      "source": {
                        "agency": "CPSC",
                        "official_id": "27064",
                        "official_url": "https://www.cpsc.gov/Recalls/2026/example",
                        "last_updated": "2026-11-13"
                      }
                    }
                  ],
                  "meta": {
                    "query": "bunk bed",
                    "total": 1,
                    "capped": false,
                    "page": 1,
                    "per_page": 20,
                    "total_pages": 1
                  },
                  "attribution": {
                    "text": "Data from RecallDB",
                    "url": "https://recalldb.app"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key suspended, or a feature not in your plan (plan_limit)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit (rate_limited) or monthly quota (quota_exceeded) reached. The message says which and when to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/v1/barcode/{code}": {
      "get": {
        "operationId": "getBarcode",
        "summary": "Look up a barcode",
        "description": "Send any barcode number: UPC-A (12 digits), EAN-13, GTIN-14, or the short UPC-E and EAN-8, with or without spaces and dashes. We check the last digit (the check digit), turn the number into one standard form, and return the recalls whose official notice lists it. A number whose check digit is wrong is refused, because it is not a real barcode.\n\nA match means the code appears in a notice. Notices often list a barcode for a whole product line while only certain lots or dates are recalled, so each match includes the lot codes, best-by dates and serial numbers written near the barcode, exactly as the notice words them. We never compare them with your item and never say an item is or is not affected.\n\nNo match is not an answer about your item: not every recall lists barcodes, so also search by product name with /v1/search.\n\nCounts as one request. A bad check digit returns 400 with code bad_barcode.\n\nThe lot, best-by and serial lists are the notice's own words found near the barcode; they can be empty even when the notice lists lots elsewhere, so always read the official notice.",
        "tags": [
          "Recalls"
        ],
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "description": "A barcode number, 8, 12, 13 or 14 digits. Spaces and dashes are ignored.",
            "schema": {
              "type": "string"
            },
            "example": "036000291452"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "id": "cpsc-27064",
                      "title": "Example Widget Recall: CPSC Recall 27064",
                      "issued_by": "Example Co",
                      "date": "2026-10-29",
                      "listed_as": "036000291452",
                      "official_text": "Example Widget 16 oz, UPC 036000291452, Lot 123A, Best By 06/30/2027.",
                      "lot_codes": [
                        "123A"
                      ],
                      "best_by_dates": [
                        "06/30/2027"
                      ],
                      "serial_numbers": [],
                      "page_url": "https://checkrecalls.org/recall/cpsc-27064",
                      "source": {
                        "agency": "CPSC",
                        "official_id": "27064",
                        "official_url": "https://www.cpsc.gov/Recalls/2026/example",
                        "last_updated": "2026-11-13"
                      }
                    }
                  ],
                  "meta": {
                    "barcode": {
                      "upcA": "036000291452",
                      "ean13": "0036000291452",
                      "gtin14": "00036000291452"
                    },
                    "matches": 1,
                    "note": "This barcode matches a code listed in the notice. Verify against the official notice: often only certain lots, dates or serial numbers are recalled. Check your lot code: compare the lot, date and serial codes on your item with the codes the official notice lists."
                  },
                  "attribution": {
                    "text": "Data from RecallDB",
                    "url": "https://recalldb.app"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key suspended, or a feature not in your plan (plan_limit)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit (rate_limited) or monthly quota (quota_exceeded) reached. The message says which and when to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/v1/new": {
      "get": {
        "operationId": "listNewRecalls",
        "summary": "New recalls feed",
        "description": "Returns the recalls we first saw on or after since (default: the last 7 days, never more than 30), newest first. Each has a stable id, so a tool can skip ones it already has. Filter with category and agency.\n\nThis is the endpoint Zapier, Make and n8n use as a \"New recall\" trigger: poll it, treat each id as a new item once. For instant delivery instead of polling, use a webhook (see the Webhooks docs).\n\nEach call counts as one request against your quota: a poll every 15 minutes is about 2,900 requests a month, so poll no faster than once an hour on a free key, or use a webhook.\n\nFree keys get this feed from a copy that is up to 1 hour old (meta.cache says when it was made). A live feed is a planned Pro feature.\n\nfirst_seen is when we first saw the recall, which can be days after the agency's date.",
        "tags": [
          "Recalls"
        ],
        "parameters": [
          {
            "name": "since",
            "in": "query",
            "required": false,
            "description": "A date or time, like 2026-11-01 or 2026-11-01T12:00:00Z. Default: 7 days ago. Older than 30 days is treated as 30 days.",
            "schema": {
              "type": "string"
            },
            "example": "2026-11-01"
          },
          {
            "name": "category",
            "in": "query",
            "required": false,
            "description": "Only this category, as in /v1/search (vehicle, consumer_product, food, drug, and so on).",
            "schema": {
              "type": "string"
            },
            "example": "consumer_product"
          },
          {
            "name": "agency",
            "in": "query",
            "required": false,
            "description": "Only this agency: nhtsa, cpsc, fda, usda, tc, hc, cfia, fsa, accc, eusg, opss or mhra.",
            "schema": {
              "type": "string"
            },
            "example": "cpsc"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many, 1 to 50 (default 20).",
            "schema": {
              "type": "string"
            },
            "example": "5"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "id": "cpsc-27064",
                      "title": "Arlopu Slide Playset Recall: CPSC Recall 27064",
                      "category": "consumer_product",
                      "issued_by": "Arlopu",
                      "date": "2026-10-29",
                      "first_seen": "2026-11-13T10:02:11.000Z",
                      "units": "About 4,000",
                      "units_affected": 4000,
                      "page_url": "https://checkrecalls.org/recall/cpsc-27064",
                      "source": {
                        "agency": "CPSC",
                        "official_id": "27064",
                        "official_url": "https://www.cpsc.gov/Recalls/2026/example",
                        "last_updated": "2026-11-13"
                      }
                    }
                  ],
                  "meta": {
                    "since": "2026-11-06T12:00:00.000Z",
                    "count": 1,
                    "note": "Newest first by when we first saw each recall. Use id to skip ones you already have."
                  },
                  "attribution": {
                    "text": "Data from RecallDB",
                    "url": "https://recalldb.app"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key suspended, or a feature not in your plan (plan_limit)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit (rate_limited) or monthly quota (quota_exceeded) reached. The message says which and when to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/v1/companies/{id}": {
      "get": {
        "operationId": "getCompany",
        "summary": "Recalls by company",
        "description": "The id is the company address on checkrecalls.org (/company/ford-motor). Companies need at least two recalls. Names are matched as the agencies wrote them (case, punctuation and endings like Inc or LLC are ignored); companies with similar names can be listed separately.\n\nA listing is a notice that names the company. It does not say whether an item is or is not affected.\n\nCounts as one request. A company with fewer than two recalls returns 404.",
        "tags": [
          "Recalls"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The company id from its checkrecalls.org address.",
            "schema": {
              "type": "string"
            },
            "example": "ford-motor"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "id": "ford-motor",
                    "name": "Ford Motor Company",
                    "total_recalls": 412,
                    "by_agency": [
                      {
                        "agency": "NHTSA",
                        "recalls": 405
                      },
                      {
                        "agency": "CPSC",
                        "recalls": 7
                      }
                    ],
                    "older_not_listed": 312,
                    "page_url": "https://checkrecalls.org/company/ford-motor",
                    "recalls": [
                      {
                        "id": "26V120000",
                        "title": "Recall 26V120000: Ford Motor Company Brakes (NHTSA)",
                        "agency": "NHTSA",
                        "date": "2026-10-05",
                        "page_url": "https://checkrecalls.org/recall/26v120000",
                        "source": {
                          "agency": "NHTSA",
                          "official_id": "26V120000",
                          "last_updated": "2026-11-13"
                        }
                      }
                    ]
                  },
                  "meta": {
                    "note": "Matched by the company name the agency wrote. Similar names can be listed separately."
                  },
                  "attribution": {
                    "text": "Data from RecallDB",
                    "url": "https://recalldb.app"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key suspended, or a feature not in your plan (plan_limit)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit (rate_limited) or monthly quota (quota_exceeded) reached. The message says which and when to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/v1/vins/batch": {
      "post": {
        "operationId": "checkVinsBatch",
        "summary": "Check many VINs",
        "description": "Send up to 5 VINs per call on a free key (each VIN counts as one request); up to 100 per call is planned for Pro. Each VIN is checked (17 characters, no I, O or Q, and the North American check digit), decoded with NHTSA vPIC, and matched to the NHTSA recalls filed for its make, model and model year, with the remedy as NHTSA wrote it.\n\nThis lists recalls filed for the model year. It does not say whether one exact vehicle still has an open recall: no official free API offers that, so check each VIN at nhtsa.gov/recalls or with a dealer. VINs are used in memory only and are never logged or stored.\n\nstatus is one of ok, invalid, check_digit_mismatch, not_decoded, not_covered, unavailable. None of them means a vehicle has no recall.\n\nMore VINs than your plan allows per call answers 400 batch_too_large; a call that would pass the monthly quota answers 429 quota_exceeded before anything is counted.",
        "tags": [
          "Recalls"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "vin": "1HGCM82633A004352",
                      "status": "ok",
                      "message": "Recalls filed for this make, model and year. Not every vehicle of the model year is part of each recall: check this VIN with NHTSA or a dealer to see whether this vehicle has an open recall.",
                      "vehicle": {
                        "year": 2003,
                        "make": "Honda",
                        "model": "Accord"
                      },
                      "model_year_recall_count": 1,
                      "recalls": [
                        {
                          "id": "03V123000",
                          "date": "2003-04-01",
                          "component": "AIR BAGS",
                          "summary": "The air bag may not deploy.",
                          "remedy": "Dealers will replace the inflator, free of charge.",
                          "severity": "standard",
                          "page_url": "https://checkrecalls.org/recall/03v123000",
                          "official_url": "https://www.nhtsa.gov/recalls?nhtsaId=03V123000"
                        }
                      ],
                      "check_this_vin": "https://www.nhtsa.gov/recalls"
                    },
                    {
                      "vin": "5YJ3E1EA7KF317001",
                      "status": "check_digit_mismatch",
                      "message": "The 9th character (the check digit) does not match the rest of the VIN, so it was probably mistyped.",
                      "vehicle": null,
                      "model_year_recall_count": null,
                      "recalls": [],
                      "check_this_vin": "https://www.nhtsa.gov/recalls"
                    }
                  ],
                  "meta": {
                    "vins": 2,
                    "counted_requests": 2
                  },
                  "source": {
                    "agency": "NHTSA",
                    "official_id": null,
                    "official_url": "https://vpic.nhtsa.dot.gov/",
                    "last_updated": "2026-11-13"
                  },
                  "attribution": {
                    "text": "Data from RecallDB",
                    "url": "https://recalldb.app"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key suspended, or a feature not in your plan (plan_limit)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit (rate_limited) or monthly quota (quota_exceeded) reached. The message says which and when to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "vins"
                ],
                "properties": {
                  "vins": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "The VINs, 17 characters each."
                  }
                }
              },
              "example": {
                "vins": [
                  "1HGCM82633A004352",
                  "5YJ3E1EA7KF317000"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/vin": {
      "post": {
        "operationId": "decodeVin",
        "summary": "Decode a VIN",
        "description": "Decodes a 17-character VIN with NHTSA vPIC and points to the model's recalls. The VIN is sent in the request body (never in a URL), is used in memory only, and is never stored, logged or echoed.\n\nThis identifies the model. It does not say whether that exact vehicle has an open recall: check the VIN with NHTSA (nhtsa.gov/recalls) or the carmaker.\n\nstatus is one of ok, none-found, not-decoded, not-covered, unavailable. none-found never means a vehicle has no recalls.",
        "tags": [
          "Recalls"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "status": "ok",
                    "vehicle": {
                      "year": 2019,
                      "make": "Honda",
                      "model": "Civic"
                    },
                    "model_recalls_page": "https://checkrecalls.org/cars/honda/civic/2019",
                    "note": "This decodes the vehicle and finds its model's recalls. It does not say whether this exact vehicle has an open recall."
                  },
                  "source": {
                    "agency": "NHTSA",
                    "official_id": null,
                    "official_url": "https://vpic.nhtsa.dot.gov/",
                    "last_updated": "2026-11-13"
                  },
                  "attribution": {
                    "text": "Data from RecallDB",
                    "url": "https://recalldb.app"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key suspended, or a feature not in your plan (plan_limit)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit (rate_limited) or monthly quota (quota_exceeded) reached. The message says which and when to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "vin"
                ],
                "properties": {
                  "vin": {
                    "type": "string",
                    "minLength": 17,
                    "maxLength": 17,
                    "description": "A 17-character VIN (letters and digits, no I, O or Q)."
                  }
                }
              },
              "example": {
                "vin": "1HGCM82633A004352"
              }
            }
          }
        }
      }
    },
    "/v1/usage": {
      "get": {
        "operationId": "getUsage",
        "summary": "Your usage",
        "description": "Returns how many requests the key has used this month, the monthly limit, how many are left, and when the count resets. Calling this endpoint does not count against your quota and is not part of the fair-use checks, and it still answers when the quota is used up.\n\nThe same numbers are in the X-RateLimit-* headers of every keyed response.",
        "tags": [
          "Recalls"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "key_id": "8f32c2ee",
                    "plan": "free",
                    "period": "2026-11",
                    "used": 120,
                    "limit": 1000,
                    "remaining": 880,
                    "resets_at": "2026-12-01T00:00:00.000Z",
                    "burst_limit_per_minute": 60,
                    "key_status": "active"
                  },
                  "attribution": {
                    "text": "Data from RecallDB",
                    "url": "https://recalldb.app"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key suspended, or a feature not in your plan (plan_limit)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit (rate_limited) or monthly quota (quota_exceeded) reached. The message says which and when to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/v1/health": {
      "get": {
        "operationId": "getHealth",
        "summary": "Data freshness",
        "description": "Returns each source and when it last refreshed successfully. A source marked stale has not refreshed within its window; treat its absence-of-recalls answers with care.",
        "tags": [
          "Status"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "sources": [
                    {
                      "id": "nhtsa",
                      "stale": false,
                      "lastSuccessAt": "2026-11-13T10:00:12.000Z"
                    },
                    {
                      "id": "cpsc",
                      "stale": false,
                      "lastSuccessAt": "2026-11-13T09:31:02.000Z"
                    },
                    {
                      "id": "fda",
                      "stale": false,
                      "lastSuccessAt": "2026-11-13T09:31:30.000Z"
                    }
                  ],
                  "attribution": {
                    "text": "Data from RecallDB",
                    "url": "https://recalldb.app"
                  }
                }
              }
            }
          }
        },
        "security": []
      }
    },
    "/v1/watchlists": {
      "post": {
        "operationId": "createWatchlist",
        "summary": "Create a watchlist",
        "description": "A watchlist is the list of things you want to hear about. Add at least one of makes, models, brands, categories or upcs (up to 10 values in all on a free key). When a recall that mentions any of them is added or changed, the watchlist's webhook is sent a message.\n\nA match means the notice mentions your value. It never says that your item is or is not affected: your customers should read the official notice and compare lot codes and dates.\n\nFree keys can have 1 watchlist of up to 10 values and 1 webhook; unlimited watchlists are a planned Pro feature (see pricing). A second watchlist, or too many values, answers 403 plan_limit or 400.\n\nCounts as one request.",
        "tags": [
          "Webhooks"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "id": "wl_8c1d2e3f4a5b",
                    "name": "My brands",
                    "makes": [],
                    "models": [],
                    "brands": [
                      "Graco"
                    ],
                    "categories": [
                      "car_seat"
                    ],
                    "upcs": [],
                    "webhook_id": "wh_3f9a1c07b2d4",
                    "created_at": "2026-11-13T12:00:00.000Z"
                  },
                  "note": "Matching recalls will be sent to your webhook."
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key suspended, or a feature not in your plan (plan_limit)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit (rate_limited) or monthly quota (quota_exceeded) reached. The message says which and when to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "A name for the watchlist, 1 to 60 characters."
                  },
                  "makes": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Vehicle makes, as on checkrecalls.org (for example Honda). Matches recalls that list that make."
                  },
                  "models": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Vehicle models. Matches the model name exactly (Civic matches Civic, not Civic Type R)."
                  },
                  "brands": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Brand or company names. Matches whole words in the notice title or the issuing company."
                  },
                  "categories": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Categories as used by /v1/search: vehicle, motorcycle, rv, car_seat, tire, equipment, other_vehicle, consumer_product, food, dietary_supplement, pet_food, cosmetic, drug, medical_device."
                  },
                  "upcs": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Barcode numbers (UPC-A, EAN-13, GTIN-14, with a correct check digit). Matches notices that list the barcode."
                  },
                  "webhook_id": {
                    "type": "string",
                    "description": "The id of one of your webhooks. Matches are sent there."
                  }
                }
              },
              "example": {
                "name": "My brands",
                "brands": [
                  "Graco"
                ],
                "categories": [
                  "car_seat"
                ],
                "webhook_id": "wh_3f9a1c07b2d4"
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listWatchlists",
        "summary": "List your watchlists",
        "description": "Returns your watchlists with their values and webhook, and the limits of your plan.\n\nCounts as one request.",
        "tags": [
          "Webhooks"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "id": "wl_8c1d2e3f4a5b",
                      "name": "My brands",
                      "makes": [],
                      "models": [],
                      "brands": [
                        "Graco"
                      ],
                      "categories": [
                        "car_seat"
                      ],
                      "upcs": [],
                      "webhook_id": "wh_3f9a1c07b2d4",
                      "created_at": "2026-11-13T12:00:00.000Z"
                    }
                  ],
                  "limits": {
                    "watchlists": 1,
                    "webhooks": 1,
                    "values_per_watchlist": 50,
                    "plan": "free"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key suspended, or a feature not in your plan (plan_limit)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit (rate_limited) or monthly quota (quota_exceeded) reached. The message says which and when to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/v1/watchlists/{id}": {
      "delete": {
        "operationId": "deleteWatchlist",
        "summary": "Delete a watchlist",
        "description": "Deletes the watchlist and its values. The webhook is kept.\n\nCounts as one request.",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The watchlist id, such as wl_8c1d2e3f4a5b.",
            "schema": {
              "type": "string"
            },
            "example": "wl_8c1d2e3f4a5b"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "id": "wl_8c1d2e3f4a5b",
                    "deleted": true
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key suspended, or a feature not in your plan (plan_limit)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit (rate_limited) or monthly quota (quota_exceeded) reached. The message says which and when to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/v1/webhooks": {
      "post": {
        "operationId": "createWebhook",
        "summary": "Create a webhook",
        "description": "Register a public HTTPS address (port 443, no IP addresses or local names). The response includes a secret that is shown only once; keep it to check the signature on every message.\n\nEach message is a POST with a JSON body and these headers: X-RecallDB-Event (recall.added, recall.changed or test), X-RecallDB-Delivery, X-RecallDB-Timestamp (Unix seconds) and X-RecallDB-Signature, which looks like t=1763035200,v1=<hex>. The signature is the HMAC-SHA256 of the timestamp, a dot and the exact body text, keyed with your secret. Recompute it, compare it in constant time, and reject messages whose timestamp is more than 5 minutes old.\n\nAnswer with any 2xx status within 8 seconds. Redirects are not followed. Anything else is retried up to 6 times in all, waiting 1, 5, 30, 120 and 360 minutes. Use X-RecallDB-Delivery or the body id to ignore a message you already handled. A webhook that fails 30 times in a row is switched off.\n\nNode example to check a message: compute crypto.createHmac(\"sha256\", secret).update(`${t}.${rawBody}`).digest(\"hex\") and compare it with v1.\n\nFree keys can have 1 webhook. Its messages are sent together once an hour (at the top of the hour, UTC), each still as its own signed POST; delivery within minutes is a planned Pro feature.\n\nCounts as one request.",
        "tags": [
          "Webhooks"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "id": "wh_3f9a1c07b2d4",
                    "url": "https://example.com/recalls",
                    "status": "active",
                    "secret": "whsec_9b2c...",
                    "secret_note": "Keep this secret. It is shown only now."
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key suspended, or a feature not in your plan (plan_limit)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit (rate_limited) or monthly quota (quota_exceeded) reached. The message says which and when to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "The https address that receives the messages."
                  }
                }
              },
              "example": {
                "url": "https://example.com/recalls"
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listWebhooks",
        "summary": "List your webhooks",
        "description": "Returns your webhooks with their status (active or disabled) and consecutive failures. Secrets are never shown again.\n\nCounts as one request.",
        "tags": [
          "Webhooks"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "id": "wh_3f9a1c07b2d4",
                      "url": "https://example.com/recalls",
                      "status": "active",
                      "consecutive_failures": 0,
                      "disabled_reason": null,
                      "created_at": "2026-11-13T12:00:00.000Z"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key suspended, or a feature not in your plan (plan_limit)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit (rate_limited) or monthly quota (quota_exceeded) reached. The message says which and when to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/v1/webhooks/{id}": {
      "delete": {
        "operationId": "deleteWebhook",
        "summary": "Delete a webhook",
        "description": "Deletes the webhook and its delivery log. Watchlists that sent to it are kept and have no webhook until you create one.\n\nCounts as one request.",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The webhook id, such as wh_3f9a1c07b2d4.",
            "schema": {
              "type": "string"
            },
            "example": "wh_3f9a1c07b2d4"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "id": "wh_3f9a1c07b2d4",
                    "deleted": true
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key suspended, or a feature not in your plan (plan_limit)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit (rate_limited) or monthly quota (quota_exceeded) reached. The message says which and when to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/v1/webhooks/{id}/test": {
      "post": {
        "operationId": "testWebhook",
        "summary": "Send a test message",
        "description": "Queues a message of type test with no recall attached. It is sent within about 5 minutes, signed like every other message, and shows in the delivery log.\n\nCounts as one request.",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The webhook id.",
            "schema": {
              "type": "string"
            },
            "example": "wh_3f9a1c07b2d4"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "event_id": "test_a1b2c3d4e5f6",
                    "queued": true,
                    "note": "The test message is sent within about 5 minutes."
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key suspended, or a feature not in your plan (plan_limit)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit (rate_limited) or monthly quota (quota_exceeded) reached. The message says which and when to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/v1/webhooks/{id}/deliveries": {
      "get": {
        "operationId": "listWebhookDeliveries",
        "summary": "Delivery log",
        "description": "Returns the last 50 deliveries with their state (pending, delivered or failed), number of attempts, the last response status or error and the time of the next retry. Entries are kept for 30 days.\n\nCounts as one request.",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The webhook id.",
            "schema": {
              "type": "string"
            },
            "example": "wh_3f9a1c07b2d4"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "id": "dl_0a1b2c3d4e5f6a7b",
                      "event_id": "evt_cpsc-27064_20261113120100",
                      "type": "recall.added",
                      "state": "delivered",
                      "attempts": 1,
                      "next_attempt_at": null,
                      "last_response_status": 200,
                      "last_error": null,
                      "created_at": "2026-11-13T12:05:00.000Z",
                      "delivered_at": "2026-11-13T12:05:01.000Z"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key suspended, or a feature not in your plan (plan_limit)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit (rate_limited) or monthly quota (quota_exceeded) reached. The message says which and when to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "description": "Requests allowed this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests left this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix time the monthly count resets (the 1st of next month, UTC).",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Policy": {
                "description": "The monthly quota and the per-minute burst limit, for example \"1000;w=2592000, 10;w=60\".",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Free key from https://recalldb.app/signup, sent as Authorization: Bearer rdb_..."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "bad_request",
                  "bad_barcode",
                  "bad_vin",
                  "missing_key",
                  "invalid_key",
                  "revoked_key",
                  "suspended_key",
                  "plan_limit",
                  "not_found",
                  "rate_limited",
                  "quota_exceeded",
                  "unavailable"
                ]
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      }
    }
  }
}