openapi: 3.0.3
info:
  title: Zipwise Web Services - radius
  description: >
    Returns all ZIP codes within a given radius (in miles) of a starting ZIP
    code, using the approximate geographic center of each ZIP code, sorted by
    distance.
  version: "2026-04"
servers:
  - url: https://www.zipwise.com/webservices

paths:
  /radius.php:
    get:
      operationId: radius
      summary: Find ZIP codes within a radius of a starting ZIP code
      description: >
        Accepts GET or POST. Counts against the account's daily call limit
        (validated via the shared account_check logic), which also enforces
        that the requested radius does not exceed the account's max_radius
        for its service tier. Results are cached server-side per
        zip/radius/format/page/country combination.
      parameters:
        - name: key
          in: query
          required: true
          description: Your 16-character API key.
          schema:
            type: string
            pattern: '^[a-z0-9]{16}$'
        - name: zip
          in: query
          required: true
          description: >
            Starting ZIP/postal code. US ZIPs are 5 digits. Canadian postal
            codes are 6 characters entered with no space; the space is
            inserted automatically before lookup.
          schema:
            type: string
        - name: radius
          in: query
          required: true
          description: Search radius in miles. Rounded to the nearest whole number. Must not exceed the account's max_radius.
          schema:
            type: number
        - name: country
          in: query
          required: false
          description: Restrict results to one country. `U` for US, `C` for Canada. Omit for both.
          schema:
            type: string
            enum: [U, C]
        - name: page
          in: query
          required: false
          description: >
            Zero-based page of results. Each page holds up to 250 results
            (page 0 = results 1-250, page 1 = results 251-500, etc.).
          schema:
            type: integer
            default: 0
            minimum: 0
        - name: format
          in: query
          required: false
          description: Response format. Anything other than `json` (including omitted) returns XML.
          schema:
            type: string
            enum: [xml, json]
            default: xml
      responses:
        "200":
          description: >
            Always returns HTTP 200. Successful requests and errors are both
            distinguished by the response body, not by HTTP status.
          content:
            text/xml:
              schema:
                $ref: '#/components/schemas/RadiusResultXml'
              examples:
                success:
                  summary: Successful result (one <result> per matching ZIP code, up to 250)
                  value: |
                    <?xml version="1.0" encoding="UTF-8"?>
                    <results>
                      <result>
                        <zip>90210</zip>
                        <city>Beverly Hills</city>
                        <county>Los Angeles</county>
                        <state>CA</state>
                        <distance>0.0</distance>
                      </result>
                      <result>
                        <zip>90213</zip>
                        <city>Beverly Hills</city>
                        <county>Los Angeles</county>
                        <state>CA</state>
                        <distance>1.9</distance>
                      </result>
                    </results>
                error:
                  summary: Error result
                  value: |
                    <?xml version="1.0" encoding="UTF-8"?>
                    <results>
                      <error>Description of the error</error>
                    </results>
            application/json:
              schema:
                $ref: '#/components/schemas/RadiusResultJson'
              examples:
                success:
                  summary: Successful result
                  value:
                    results:
                      - zip: "90210"
                        city: Beverly Hills
                        county: Los Angeles
                        state: CA
                        distance: "0.0"
                      - zip: "90213"
                        city: Beverly Hills
                        county: Los Angeles
                        state: CA
                        distance: "1.9"
                error:
                  summary: Error result
                  value:
                    results:
                      error: Description of the error

components:
  schemas:
    RadiusResultXml:
      type: string
      description: XML document with a `<results>` root containing zero or more `<result>` elements, or a single `<error>` element.

    RadiusResultJson:
      type: object
      properties:
        results:
          oneOf:
            - type: array
              items:
                type: object
                properties:
                  zip:
                    type: string
                  city:
                    type: string
                  county:
                    type: string
                  state:
                    type: string
                  distance:
                    type: string
                    description: Distance from the starting ZIP code in miles, to one decimal place.
            - type: object
              properties:
                error:
                  type: string

x-notes:
  - "Possible error messages: '{zip} is not a recognizable ZIP code.', 'You did not provide a ZIP code.', '{key} is not a recognizable API key.', 'You did not provide an API key.', 'You did not provide a radius.', '{country} is an invalid country.', '{zip} is not a valid starting ZIP code.', '{zip} is not the type of ZIP code that has a specific geographic location, so there are no results for this search.', plus the shared account-limit errors, including 'Your account has a radius limit of {max_radius} miles...' when the requested radius exceeds the account's tier."
  - "The published documentation (radius.pdf) states this method is limited to one request per second on the free tier; that enforcement is currently disabled in the deployed code (commented out), so it is not presently active."
  - "Each response page returns at most 250 results, ordered by distance ascending."
