Autofill city and state from a ZIP code

Mark a US ZIP field with form.js and fill editable city and state fields without writing lookup code.

JavaScript (browser)Last tested Get the complete example ↓
On this page

Working example

HTML + form.js
Use this example

Connect the three fields

Include form.js, mark the ZIP input, and bind your city and state fields to its answer. data-parse-from="postal" names the source input; data-parse-fill names the response field to fill.

HTML
<script src="https://cdn.parseapi.com/v1/form.js" data-key="YOUR_PUBLIC_API_KEY" defer></script>
<div class="field">
  <label for="postal">US ZIP code</label>
  <input id="postal" name="postal" data-parse="postal" data-parse-country="US" inputmode="numeric" autocomplete="postal-code" maxlength="5" placeholder="90210">
</div>
<div class="row">
  <div class="field">
    <label for="city">City</label>
    <input id="city" name="city" data-parse-from="postal" data-parse-fill="city" autocomplete="address-level2">
  </div>
  <div class="field">
    <label for="state">State</label>
    <input id="state" name="state" data-parse-from="postal" data-parse-fill="state" autocomplete="address-level1">
  </div>
</div>

Replace YOUR_PUBLIC_API_KEY with a Public key from the example setup. Allow your site's hostname and localhost for local development. Serve the downloaded HTML over HTTP; keep secret keys on your server.

The ZIP stays text, so leading zeroes survive. inputmode="numeric" offers a numeric keyboard. data-parse-country="US" makes this example's country explicit.

Enter a complete ZIP

Type five digits and pause. The script fills the primary city and state for that ZIP. There is no lookup button, and a partial ZIP does not make a request.

City and state remain editable. Correct either field when needed; a pending response will not replace a manual correction. Changing the ZIP clears old generated values so they do not describe the previous code. Missing details stay blank, and unavailable lookups leave manual entry available.

Try 90210, then change the ZIP and watch the generated values clear. These fields can sit inside your existing form and submit under the names you chose.

A ZIP lookup does not verify postal delivery to a street address. Build directly with the Postal API for a custom integration or other postal-code coverage.

Complete example

One HTML file with your fields, styles, and the form.js include. Replace YOUR_PUBLIC_API_KEY with your public key, then run it on a domain you have added to that key.

Download HTML
View and copy the complete source
zip-code-autofill.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Autofill city and state from a ZIP code</title>
  <style>
    :root { color-scheme: light dark; --bg: #fff; --ink: #152029; --muted: #5e6b75; --line: #d9e2e7; --field: #f6f9fa; --accent: #007c91; --error: #b42335; }
    @media (prefers-color-scheme: dark) {
      :root:not([data-theme="light"]) { --bg: #0b1015; --ink: #eff7fa; --muted: #94a6b2; --line: #2c3942; --field: #121b23; --accent: #67e8f9; --error: #fda4af; }
    }
    :root[data-theme="dark"] { color-scheme: dark; --bg: #0b1015; --ink: #eff7fa; --muted: #94a6b2; --line: #2c3942; --field: #121b23; --accent: #67e8f9; --error: #fda4af; }
    :root[data-theme="light"] { color-scheme: light; }
    * { box-sizing: border-box; }
    [hidden] { display: none !important; }
    body { margin: 0; padding: 24px; background: var(--bg); color: var(--ink); font: 15px/1.5 system-ui, sans-serif; }
    main { max-width: 540px; margin: 0 auto; }
    label { display: block; margin-bottom: 8px; font-size: 14px; font-weight: 600; }
    input { display: block; width: 100%; min-width: 0; padding: 13px 14px; border: 1px solid var(--line); border-radius: 8px; background: var(--field); color: var(--ink); font: inherit; }
    input::placeholder { color: var(--muted); }
    :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
    .field + .field { margin-top: 18px; }
    .row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; margin-top: 18px; }
    .row .field { margin-top: 0; }
    /* Your form owns these styles. form.js adds semantic markup and states. */
    input[data-parse-status="invalid"], input[data-parse-status="disposable"] { border-color: var(--error); }
    .parse-hint { margin: 6px 0 0; color: var(--muted); font-size: .875em; line-height: 1.4; overflow-wrap: anywhere; }
    .parse-hint[data-parse-status="invalid"], .parse-hint[data-parse-status="disposable"] { color: var(--error); }
    @media (max-width: 380px) { body { padding: 18px; } .row { grid-template-columns: 1fr; } }
  </style>
</head>
<body>
  <main>
    <!-- example:form -->
    <script src="https://cdn.parseapi.com/v1/form.js" data-key="YOUR_PUBLIC_API_KEY" defer></script>
    <div class="field">
      <label for="postal">US ZIP code</label>
      <input id="postal" name="postal" data-parse="postal" data-parse-country="US" inputmode="numeric" autocomplete="postal-code" maxlength="5" placeholder="90210">
    </div>
    <div class="row">
      <div class="field">
        <label for="city">City</label>
        <input id="city" name="city" data-parse-from="postal" data-parse-fill="city" autocomplete="address-level2">
      </div>
      <div class="field">
        <label for="state">State</label>
        <input id="state" name="state" data-parse-from="postal" data-parse-fill="state" autocomplete="address-level1">
      </div>
    </div>
    <!-- /example:form -->
  </main>
</body>
</html>

Need a key? Set up this browser example. For every field and parameter, see Postal API reference.

Build something else

All tutorials →