openapi: 3.0.3
info:
  title: Zipwise Web Services - citysearch
  description: >
    Returns all ZIP codes whose city name contains a given search string,
    optionally restricted to a single state.
  version: "2026-04"
servers:
  - url: https://www.zipwise.com/webservices

paths:
  /citysearch.php:
    get:
      operationId: citySearch
      summary: Search ZIP codes by city name substring
      description: >
        Accepts GET or POST. Counts against the account's daily call limit
        (validated via the shared account_check logic). Rate-limited per
        session to one call every 0.5 seconds.
      parameters:
        - name: key
          in: query
          required: true
          description: Your 16-character API key.
          schema:
            type: string
            pattern: '^[a-z0-9]{16}$'
        - name: string
          in: query
          required: true
          description: >
            Substring to match against city names (case-insensitive `LIKE '%string%'`).
            Only letters, digits, spaces, and periods are honored; other characters
            are stripped.
          schema:
            type: string
        - name: state
          in: query
          required: false
          description: Optional 2-letter state abbreviation to restrict results to one state.
          schema:
            type: string
            pattern: '^[A-Za-z]{2}$'
        - 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/CitySearchResultXml'
              examples:
                success:
                  summary: Successful result (one <result> per matching ZIP code)
                  value: |
                    <?xml version="1.0" encoding="UTF-8"?>
                    <results>
                      <result>
                        <zip>93501</zip>
                        <city>Mojave</city>
                        <state>CA</state>
                      </result>
                      <result>
                        <zip>93502</zip>
                        <city>Mojave</city>
                        <state>CA</state>
                      </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/CitySearchResultJson'
              examples:
                success:
                  summary: Successful result
                  value:
                    results:
                      - zip: "93501"
                        city: Mojave
                        state: CA
                      - zip: "93502"
                        city: Mojave
                        state: CA
                error:
                  summary: Error result
                  value:
                    results:
                      error: Description of the error

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

    CitySearchResultJson:
      type: object
      properties:
        results:
          oneOf:
            - type: array
              items:
                type: object
                properties:
                  zip:
                    type: string
                  city:
                    type: string
                  state:
                    type: string
            - type: object
              properties:
                error:
                  type: string

x-notes:
  - "Possible error messages: 'You did not provide a valid string to search on.', '{key} is not a recognizable API key.', 'You did not provide an API key.', 'If you include a state parameter, it must be 2 characters.', 'Please wait 0.5 seconds between calls.', 'No cities were found matching the search string {string}.', plus the shared account-limit errors (invalid API key, daily call limit exceeded, failed billing reverted to free tier, abusive account, IP shared across free accounts)."
  - "Results are ordered by city, then zip."
