{
  "openapi": "3.0.3",
  "info": {
    "title": "PostFinder API",
    "version": "1.0.0",
    "description": "Two things, and deliberately only two: a typeahead over the directory, and postcode lookup.\n\nBoth answer the same questions the website answers, from the same data. No key is needed and there is nothing to buy.\n\n**Attribution travels with the data.** Locations come from OpenStreetMap (ODbL), localities and postcodes from GeoNames (CC BY 4.0), and Australian street addresses from G-NAF Core (CC BY 4.0). If you publish what you get back, you carry those credits with it. The [sources page](https://postfinder.io/en/legal/) names each one.\n\n**Please be gentle.** Responses are cacheable and a client that debounces its typing costs almost nothing. Around a thousand requests a month from one address is the guideline; if you need more, [say what you are building](https://postfinder.io/en/contact/) and we will very likely help. The [acceptable use policy](https://postfinder.io/en/acceptable-use/) has the detail.",
    "contact": {
      "name": "PostFinder",
      "url": "https://postfinder.io/en/contact/"
    }
  },
  "servers": [
    {
      "url": "https://api.postfinder.io",
      "description": "Production"
    }
  ],
  "paths": {
    "/v1/search": {
      "get": {
        "summary": "Suggest suburbs, places and addresses as someone types",
        "description": "The site's own search box, as an endpoint.\n\nTwo characters minimum: below that it returns an empty list rather than matching most of the table. Debounce by at least 150ms in the client, since firing on every keystroke spends bandwidth for no better answer.\n\nRows come back most useful first, and each carries enough to build a link to the page it names without a second request.",
        "operationId": "search",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 2
            },
            "description": "What has been typed so far.",
            "example": "coburg"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 10,
              "maximum": 25
            },
            "description": "How many rows to return."
          }
        ],
        "responses": {
          "200": {
            "description": "Matching rows, best first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SearchHit"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The query was missing or too short."
          }
        }
      }
    },
    "/v1/countries/{country}/postcodes": {
      "get": {
        "summary": "Every postcode in a country, grouped by state",
        "description": "The whole index in one response, which is how a postcode list is read and what makes it cacheable. Large for a big country: fetch it once and keep it rather than asking per keystroke.",
        "operationId": "postcodes",
        "parameters": [
          {
            "name": "country",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Country slug as the site's URLs use it: australia, united-kingdom, united-states, new-zealand.",
            "example": "australia"
          }
        ],
        "responses": {
          "200": {
            "description": "The index.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "country": {
                      "type": "object"
                    },
                    "total": {
                      "type": "integer"
                    },
                    "regions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "region": {
                            "$ref": "#/components/schemas/Region"
                          },
                          "entries": {
                            "type": "array",
                            "items": {
                              "$ref": "#/components/schemas/PostcodeEntry"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such country."
          }
        }
      }
    },
    "/v1/countries/{country}/postcodes/{postcode}": {
      "get": {
        "summary": "Resolve one postcode to the suburbs it covers",
        "description": "A postcode is not a suburb. 3058 is Coburg, Coburg North and Merlynston, and an address in any of them is written with the same four digits, so this returns all of them, busiest first.",
        "operationId": "postcodeDetail",
        "parameters": [
          {
            "name": "country",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Country slug as the site's URLs use it: australia, united-kingdom, united-states, new-zealand.",
            "example": "australia"
          },
          {
            "name": "postcode",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Three to five digits.",
            "example": "3058"
          }
        ],
        "responses": {
          "200": {
            "description": "The suburbs this postcode covers.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "country": {
                      "type": "object"
                    },
                    "region": {
                      "$ref": "#/components/schemas/Region"
                    },
                    "postcode": {
                      "type": "string"
                    },
                    "total": {
                      "type": "integer"
                    },
                    "localities": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PostcodeEntry"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Nothing is filed under that postcode."
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "SearchHit": {
        "type": "object",
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "locality",
              "place",
              "address"
            ],
            "description": "What the row is: a suburb, a location, or a street address."
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string",
            "description": "The URL segment for this row on the website."
          },
          "country": {
            "type": "string"
          },
          "region": {
            "type": "string"
          },
          "locality": {
            "type": "string",
            "description": "Slug of the suburb the row sits in."
          },
          "locality_name": {
            "type": "string"
          },
          "state": {
            "type": "string",
            "description": "State code, where the country has them."
          },
          "postcode": {
            "type": "string"
          },
          "place_count": {
            "type": "integer",
            "description": "How many locations a suburb holds."
          },
          "lat": {
            "type": "number"
          },
          "lng": {
            "type": "number"
          }
        },
        "required": [
          "kind",
          "name"
        ]
      },
      "PostcodeEntry": {
        "type": "object",
        "properties": {
          "postcode": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "place_count": {
            "type": "integer"
          }
        },
        "required": [
          "postcode",
          "slug",
          "name"
        ]
      },
      "Region": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        }
      }
    }
  }
}
