{
  "openapi": "3.0.3",
  "info": {
    "title": "RecallTracker - Canadian Product & Vehicle Recall API",
    "description": "Canadian product and vehicle recall API from Health Canada, CFIA, and Transport Canada: food safety alerts, consumer product recalls, and vehicle defects. Filter by recall_type, recall_agency, hazard_class, and allergen. Bilingual EN/FR. Updated every 15 minutes.",
    "version": "0.1.0"
  },
  "paths": {
    "/health": {
      "get": {
        "summary": "Health",
        "operationId": "/health",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/recall/alerts/subscribe": {
      "post": {
        "summary": "Subscribe to email alerts for new recalls matching a saved search",
        "operationId": "/recall/alerts/subscribe",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RecallAlertRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "400": {
            "description": "The request is understood but cannot be served as asked, e.g. offset above 9500, or cursor combined with sort_by=value. The message names the fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such endpoint, or no record with that id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "A parameter is invalid: an unknown city slug, an impossible date, a limit above 500, an unrecognised sort field, or a misspelled parameter name. The message names the parameter and, where there is one, the nearest valid value.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or plan quota exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "The query took too long. Narrow it with a municipality or a date range.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/recall/alerts/unsubscribe": {
      "get": {
        "summary": "Unsubscribe from recall alerts",
        "operationId": "/recall/alerts/unsubscribe",
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Token"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "400": {
            "description": "The request is understood but cannot be served as asked, e.g. offset above 9500, or cursor combined with sort_by=value. The message names the fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such endpoint, or no record with that id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "A parameter is invalid: an unknown city slug, an impossible date, a limit above 500, an unrecognised sort field, or a misspelled parameter name. The message names the parameter and, where there is one, the nearest valid value.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or plan quota exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "The query took too long. Narrow it with a municipality or a date range.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/recall/products": {
      "get": {
        "summary": "Products and affected models for a recall",
        "operationId": "/recall/products",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "description": "record_id from a recall record",
              "title": "Id"
            },
            "description": "record_id from a recall record"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {},
                "example": {
                  "recall_id": "2024-076-HC",
                  "recall_type": "health_product",
                  "product_list": [
                    {
                      "name": "Tylenol Extra Strength 500mg",
                      "upc": "05631234567890"
                    }
                  ],
                  "affected_models": null,
                  "distribution": "National",
                  "units_affected": null,
                  "identification_number": "DIN 00559407",
                  "manufacturer_recall_number": null,
                  "system": null
                }
              }
            }
          },
          "400": {
            "description": "The request is understood but cannot be served as asked, e.g. offset above 9500, or cursor combined with sort_by=value. The message names the fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such endpoint, or no record with that id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "A parameter is invalid: an unknown city slug, an impossible date, a limit above 500, an unrecognised sort field, or a misspelled parameter name. The message names the parameter and, where there is one, the nearest valid value.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or plan quota exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "The query took too long. Narrow it with a municipality or a date range.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/recall/upc": {
      "get": {
        "summary": "Product recalls matching a UPC or barcode",
        "operationId": "/recall/upc",
        "parameters": [
          {
            "name": "upc",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "description": "UPC, EAN, or GTIN barcode (digits only)",
              "title": "Upc"
            },
            "description": "UPC, EAN, or GTIN barcode (digits only)"
          },
          {
            "name": "lang",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "nullable": true
            },
            "description": "Language filter: en or fr"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {},
                "example": {
                  "upc": "05631234567890",
                  "digits": "05631234567890",
                  "count": 1,
                  "results": [
                    {
                      "recall_id": "2024-076-HC",
                      "recall_type": "health_product",
                      "title_en": "Recall example"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request is understood but cannot be served as asked, e.g. offset above 9500, or cursor combined with sort_by=value. The message names the fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such endpoint, or no record with that id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "A parameter is invalid: an unknown city slug, an impossible date, a limit above 500, an unrecognised sort field, or a misspelled parameter name. The message names the parameter and, where there is one, the nearest valid value.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or plan quota exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "The query took too long. Narrow it with a municipality or a date range.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/recall/vin": {
      "get": {
        "summary": "Vehicle recalls matching a VIN",
        "operationId": "/recall/vin",
        "parameters": [
          {
            "name": "vin",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "description": "17-character Vehicle Identification Number",
              "title": "Vin"
            },
            "description": "17-character Vehicle Identification Number",
            "example": "1HGCM82633A004352"
          },
          {
            "name": "lang",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "nullable": true
            },
            "description": "Language filter: en or fr"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {},
                "example": {
                  "vin": "1HGCM82633A004352",
                  "decoded": {
                    "make": "HONDA",
                    "model": "ACCORD",
                    "year": 2003
                  },
                  "count": 1,
                  "results": [
                    {
                      "recall_id": "2024-RC-001",
                      "recall_type": "vehicle",
                      "title_en": "Airbag inflator recall"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request is understood but cannot be served as asked, e.g. offset above 9500, or cursor combined with sort_by=value. The message names the fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such endpoint, or no record with that id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "A parameter is invalid: an unknown city slug, an impossible date, a limit above 500, an unrecognised sort field, or a misspelled parameter name. The message names the parameter and, where there is one, the nearest valid value.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or plan quota exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "The query took too long. Narrow it with a municipality or a date range.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/recall": {
      "get": {
        "summary": "Search recalls",
        "operationId": "/recall",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "nullable": true
            },
            "description": "Full-text search across the indexed fields of the record, including names (business, establishment, vendor, contractor) wherever the dataset carries them, e.g. q=brewery or q=\"Iron Hill\". This is the name search: there is no separate name= parameter, and it works on every plan including free. It matches the BUSINESS or trade name, the address, city, county and licence/permit number. It does NOT match an owner, licensee or applicant's personal name: these datasets carry public business records only and never store an individual's name, so searching for a person returns nothing. Space-separated words require all terms (e.g. \"wood panel\"). Use OR to match any term (\"wood OR panel OR acoustic\"), quotes for exact phrases (\"supply arrangement\"), and a leading minus to exclude (\"software -hardware\")."
          },
          {
            "name": "category",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "nullable": true
            },
            "description": "Procurement category: CNST, GD, SRV, SRVTGD. For recalls: product category (e.g. 'Baby products', 'Toys and games'). For healthcare: facility category (acute, long_term, home, behavioral, outpatient, specialty). For storage tanks: record type (tank or release)."
          },
          {
            "name": "source_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "nullable": true
            },
            "description": "Only records from one source feed, by its id (exact), e.g. 'gatineau_tenders'."
          },
          {
            "name": "recall_type",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "nullable": true
            },
            "description": "Recall type: food, vehicle, health_product, consumer_product, medical_device, cannabis"
          },
          {
            "name": "recall_agency",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "nullable": true
            },
            "description": "Recall issuing agency: CFIA, Health Canada, Transport Canada"
          },
          {
            "name": "hazard_class",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "nullable": true
            },
            "description": "Hazard class: class_1, class_2, class_3"
          },
          {
            "name": "allergen",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "nullable": true
            },
            "description": "Filter food recalls by allergen: milk, peanut, gluten, egg, fish, sesame, soy, sulphites, tree_nut, wheat, mustard, crustacean, shellfish"
          },
          {
            "name": "issued_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "nullable": true
            },
            "description": "Filter by recall date >= YYYY-MM-DD (event_date). Aliases: date_from, date_after"
          },
          {
            "name": "issued_before",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "nullable": true
            },
            "description": "Filter by date <= YYYY-MM-DD. Aliases: date_to, date_before"
          },
          {
            "name": "sort_by",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "nullable": true
            },
            "description": "Sort field: published (publication_date, tenders only), date (event date; this is award_date for contracts/awards), value (the entity's value field), closing (closing_date for tenders)"
          },
          {
            "name": "sort_order",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "description": "Sort order: asc or desc",
              "default": "desc",
              "title": "Sort Order"
            },
            "description": "Sort order: asc or desc"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 500,
              "minimum": 1,
              "description": "Records per page, 1 to 500. Free-tier keys are capped lower; the cap applied is the `count` in the response.",
              "default": 100,
              "title": "Limit"
            },
            "description": "Records per page, 1 to 500. Free-tier keys are capped lower; the cap applied is the `count` in the response."
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 0,
              "description": "Records to skip. Fine for jumping to a known page; use `cursor` to walk a whole result set, because offset re-reads everything it skips and shifts when new records arrive.",
              "default": 0,
              "title": "Offset"
            },
            "description": "Records to skip. Fine for jumping to a known page; use `cursor` to walk a whole result set, because offset re-reads everything it skips and shifts when new records arrive."
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "nullable": true
            },
            "description": "Pagination cursor from next_cursor in a previous response. Use instead of offset for deep pagination."
          },
          {
            "name": "lang",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "nullable": true
            },
            "description": "Return translations for a single language only: en or fr."
          },
          {
            "name": "fields",
            "in": "query",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "nullable": true
            },
            "description": "Return only these fields on each record, comma-separated or repeated, e.g. fields=title_en,closing_date. record_id is always included. field is accepted as an alias."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecallRecordPage"
                },
                "example": {
                  "entity_type": "recall",
                  "count": 5,
                  "next": "eyJkIjoiMjAyNS0wMy0xMiIsImkiOiI3NzQifQ==",
                  "results": [
                    {
                      "record_id": "hc:77401",
                      "recall_type": "food",
                      "agency": "Health Canada",
                      "hazard_class": "class_1",
                      "event_date": "2025-03-12",
                      "recalling_firm": "President's Choice",
                      "allergens": [
                        "listeria"
                      ],
                      "translations": {
                        "en": {
                          "title": "President's Choice Spinach recalled due to Listeria",
                          "hazard_description": "May be contaminated with Listeria.",
                          "what_to_do": "Do not consume. Return to place of purchase."
                        },
                        "fr": {
                          "title": "Rappel \u00e9pinards President's Choice \u2014 Listeria"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request is understood but cannot be served as asked, e.g. offset above 9500, or cursor combined with sort_by=value. The message names the fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such endpoint, or no record with that id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "A parameter is invalid: an unknown city slug, an impossible date, a limit above 500, an unrecognised sort field, or a misspelled parameter name. The message names the parameter and, where there is one, the nearest valid value.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or plan quota exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "The query took too long. Narrow it with a municipality or a date range.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/recall/stats": {
      "get": {
        "summary": "Recall statistics",
        "operationId": "/recall/stats",
        "parameters": [
          {
            "name": "group_by",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "nullable": true
            },
            "description": "Data field to group by"
          },
          {
            "name": "period",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "nullable": true
            },
            "description": "A number and a unit: 30d, 2w, 6mo, 1y. The unit may be spelled out (7days, 6months, 1year), or use 'all' for everything. Defaults to all for static registries (healthcare, liquor); 30d otherwise."
          },
          {
            "name": "sum_field",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "nullable": true
            },
            "description": "Data field to sum (e.g. contract_value)"
          },
          {
            "name": "issued_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "nullable": true
            },
            "description": "Filter by recall date >= YYYY-MM-DD (event_date). Aliases: date_from, date_after"
          },
          {
            "name": "issued_before",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "nullable": true
            },
            "description": "Filter by date <= YYYY-MM-DD. Aliases: date_to, date_before"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {},
                "example": {
                  "entity_type": "recall",
                  "period_days": 30,
                  "total_in_db": 41474,
                  "by_recall_type": [
                    {
                      "value": "food",
                      "count": 7200
                    },
                    {
                      "value": "vehicle",
                      "count": 17300
                    },
                    {
                      "value": "consumer_product",
                      "count": 12100
                    },
                    {
                      "value": "health_product",
                      "count": 3800
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request is understood but cannot be served as asked, e.g. offset above 9500, or cursor combined with sort_by=value. The message names the fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such endpoint, or no record with that id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "A parameter is invalid: an unknown city slug, an impossible date, a limit above 500, an unrecognised sort field, or a misspelled parameter name. The message names the parameter and, where there is one, the nearest valid value.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or plan quota exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "The query took too long. Narrow it with a municipality or a date range.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/recall/{record_id}": {
      "get": {
        "summary": "Get recall by ID",
        "operationId": "/recall/{record_id}",
        "parameters": [
          {
            "name": "record_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Record Id"
            },
            "example": "82207"
          },
          {
            "name": "lang",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "nullable": true
            },
            "description": "Return translations for a single language only: en or fr."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {},
                "example": {
                  "record_id": "hc:77401",
                  "recall_type": "food",
                  "agency": "Health Canada",
                  "hazard_class": "class_1",
                  "event_date": "2025-03-12",
                  "recalling_firm": "President's Choice",
                  "allergens": [
                    "listeria"
                  ],
                  "translations": {
                    "en": {
                      "title": "President's Choice Spinach recalled due to Listeria",
                      "hazard_description": "May be contaminated with Listeria.",
                      "what_to_do": "Do not consume. Return to place of purchase."
                    },
                    "fr": {
                      "title": "Rappel \u00e9pinards President's Choice \u2014 Listeria"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request is understood but cannot be served as asked, e.g. offset above 9500, or cursor combined with sort_by=value. The message names the fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such endpoint, or no record with that id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "A parameter is invalid: an unknown city slug, an impossible date, a limit above 500, an unrecognised sort field, or a misspelled parameter name. The message names the parameter and, where there is one, the nearest valid value.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or plan quota exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "The query took too long. Narrow it with a municipality or a date range.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "RecallAlertRequest": {
        "properties": {
          "email": {
            "type": "string",
            "title": "Email"
          },
          "q": {
            "type": "string",
            "nullable": true
          },
          "agency": {
            "type": "string",
            "nullable": true
          },
          "recall_type": {
            "type": "string",
            "nullable": true
          },
          "hazard_class": {
            "type": "string",
            "nullable": true
          },
          "allergen": {
            "type": "string",
            "nullable": true
          },
          "category": {
            "type": "string",
            "nullable": true
          }
        },
        "type": "object",
        "required": [
          "email"
        ],
        "title": "RecallAlertRequest"
      },
      "RecallRecord": {
        "type": "object",
        "description": "One recall record. Measured over 500 live records sampled across municipalities. Percentages say how often each field is populated: sources publish different columns, so a field missing from a given record is normal rather than an error.",
        "properties": {
          "affected_models": {
            "type": "array",
            "description": "Populated in 36% of sampled records.",
            "nullable": true
          },
          "agency": {
            "type": "string",
            "description": "Always present.",
            "example": "Health Canada"
          },
          "alert_type": {
            "type": "string",
            "description": "Populated in 63% of sampled records.",
            "example": "Consumer product recall",
            "nullable": true
          },
          "categories": {
            "type": "array",
            "description": "Populated in 63% of sampled records.",
            "nullable": true
          },
          "category": {
            "type": "string",
            "description": "Populated in 64% of sampled records.",
            "example": "Other",
            "nullable": true
          },
          "companies": {
            "type": "array",
            "description": "Populated in 11% of sampled records.",
            "nullable": true
          },
          "date_modified": {
            "type": "string",
            "description": "Populated in 39% of sampled records.",
            "example": "2026-08-27",
            "nullable": true
          },
          "event_date": {
            "type": "string",
            "description": "Always present.",
            "example": "2026-09-01"
          },
          "fetched_at": {
            "type": "string",
            "description": "When Nimbus last ingested this record. Bookkeeping, not source data. Always present.",
            "example": "2026-09-03T00:40:34.509365+00:00"
          },
          "hazard_class": {
            "type": "string",
            "description": "Populated in 49% of sampled records.",
            "example": "class_1",
            "nullable": true
          },
          "identification_number": {
            "type": "string",
            "description": "Populated in 63% of sampled records.",
            "example": "RA-82577",
            "nullable": true
          },
          "image_url": {
            "type": "string",
            "description": "Populated in 18% of sampled records.",
            "example": "https://images.openfoodfacts.org/images/products/611/124/672\u2026",
            "nullable": true
          },
          "images": {
            "type": "array",
            "description": "Populated in 21% of sampled records.",
            "nullable": true
          },
          "last_updated": {
            "type": "string",
            "description": "Populated in 36% of sampled records.",
            "example": "2026-09-02",
            "nullable": true
          },
          "manufacturer_recall_number": {
            "type": "string",
            "description": "Populated in 32% of sampled records.",
            "example": "9903664",
            "nullable": true
          },
          "notification_type": {
            "type": "string",
            "description": "Populated in 36% of sampled records.",
            "example": "Compliance Mfr",
            "nullable": true
          },
          "product_list": {
            "type": "array",
            "description": "Populated in 52% of sampled records.",
            "nullable": true
          },
          "recall_class_page": {
            "type": "string",
            "description": "Populated in 18% of sampled records.",
            "example": "Class 1",
            "nullable": true
          },
          "recall_date": {
            "type": "string",
            "description": "Always present.",
            "example": "2026-09-01"
          },
          "recall_id": {
            "type": "string",
            "description": "Always present.",
            "example": "82577"
          },
          "recall_type": {
            "type": "string",
            "description": "Always present.",
            "example": "consumer_product"
          },
          "recalling_firm": {
            "type": "string",
            "description": "Populated in 12% of sampled records.",
            "example": "Laiterie de Coaticook Limit\u00e9e",
            "nullable": true
          },
          "record_id": {
            "type": "string",
            "description": "Stable identifier for this record. Pass it to the /{entity_type}/{record_id} endpoint. Always present.",
            "example": "82577"
          },
          "status": {
            "type": "string",
            "description": "Always present.",
            "example": "active"
          },
          "system": {
            "type": "string",
            "description": "Populated in 36% of sampled records.",
            "example": "Label",
            "nullable": true
          },
          "translations": {
            "type": "object",
            "description": "Always present."
          },
          "units_affected": {
            "type": "integer",
            "description": "Populated in 36% of sampled records.",
            "example": 5,
            "nullable": true
          },
          "url_en": {
            "type": "string",
            "description": "Populated in 98% of sampled records.",
            "example": "https://recalls-rappels.canada.ca/en/alert-recall/cuisinart-\u2026",
            "nullable": true
          },
          "url_fr": {
            "type": "string",
            "description": "Populated in 93% of sampled records.",
            "example": "https://recalls-rappels.canada.ca/fr/avis-rappel/brosses-pou\u2026",
            "nullable": true
          },
          "vehicle_category": {
            "type": "string",
            "description": "Populated in 36% of sampled records.",
            "example": "Motorhome",
            "nullable": true
          },
          "vehicle_make": {
            "type": "string",
            "description": "Populated in 35% of sampled records.",
            "example": "JAYCO",
            "nullable": true
          },
          "vehicle_model": {
            "type": "string",
            "description": "Populated in 35% of sampled records.",
            "example": "SENECA PRESTIGE MOTORHOME",
            "nullable": true
          },
          "vehicle_slugs": {
            "type": "array",
            "description": "Populated in 36% of sampled records.",
            "nullable": true
          }
        },
        "x-sparse-fields": {
          "allergens": {
            "type": "array",
            "fill_rate": 0.06
          },
          "cfia_id": {
            "type": "string",
            "fill_rate": 0.0
          },
          "din": {
            "type": "string",
            "fill_rate": 0.0
          },
          "distribution": {
            "type": "array",
            "fill_rate": 0.082
          },
          "importer": {
            "type": "string",
            "fill_rate": 0.024
          },
          "manufacturer": {
            "type": "string",
            "fill_rate": 0.044
          },
          "municipality": {
            "type": "string",
            "fill_rate": 0.0
          },
          "npn": {
            "type": "string",
            "fill_rate": 0.0
          },
          "place_of_origin": {
            "type": "string",
            "fill_rate": 0.078
          },
          "time_period_sold": {
            "type": "string",
            "fill_rate": 0.068
          },
          "units_sold": {
            "type": "string",
            "fill_rate": 0.072
          },
          "upc": {
            "type": "string",
            "fill_rate": 0.0
          }
        }
      },
      "RecallRecordPage": {
        "type": "object",
        "properties": {
          "entity_type": {
            "type": "string",
            "example": "recall"
          },
          "count": {
            "type": "integer",
            "description": "Records in this page."
          },
          "offset": {
            "type": "integer"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass back as `cursor` for the next page. Null on the last page."
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RecallRecord"
            }
          }
        },
        "description": "Field coverage measured 2026-09-03."
      },
      "Error": {
        "type": "object",
        "description": "Every 4xx and 5xx response has this shape.",
        "properties": {
          "detail": {
            "type": "string",
            "description": "A sentence naming what was wrong and, where possible, the fix.",
            "example": "limit must be at most 500 (got 1000). Use offset, or cursor for deep pagination, to retrieve more."
          }
        }
      }
    }
  },
  "servers": [
    {
      "url": "https://hv1nforwhi.execute-api.ca-central-1.amazonaws.com"
    }
  ]
}