# USDA Plant Hardiness Zone ZIP Lookup (`automation-lab/usda-hardiness-zone-zip-lookup`) Actor

Resolve US ZIP codes to official 2023 USDA plant hardiness zones, temperature bands, postal-centroid coordinates, statuses, and source provenance.

- **URL**: https://apify.com/automation-lab/usda-hardiness-zone-zip-lookup.md
- **Developed by:** [Stas Persiianenko](https://apify.com/automation-lab) (community)
- **Categories:** Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.41 / 1,000 zip lookup rows

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## USDA Plant Hardiness Zone ZIP Lookup

Resolve batches of US ZIP codes to official **USDA plant hardiness zones**, half-zones, minimum-temperature bands, representative coordinates, lookup statuses, and source provenance.

Use this Actor to enrich landscaping leads, nursery customer files, property portfolios, planting plans, and agricultural location data without manually entering ZIP codes into the USDA map.

### What does USDA Plant Hardiness Zone ZIP Lookup do?

The Actor accepts up to 1,000 US ZIP codes per run.

It queries the public table behind the 2023 USDA Plant Hardiness Zone Map.

For every supplied value, it emits one typed dataset row with:

- normalized five-digit ZIP code;
- USDA zone and `a` or `b` subzone;
- Fahrenheit and Celsius temperature-band bounds;
- explicit `found`, `not_found`, or `invalid` status;
- optional postal-centroid latitude and longitude;
- USDA edition, page, data endpoint, and retrieval timestamp.

ZIP+4 input is normalized to its five-digit ZIP.

Duplicate normalized ZIP codes are processed once.

### Who is it for?

**Landscaping companies** can enrich customer and service-address exports before choosing plant palettes.

**Nurseries and garden retailers** can segment mailing lists or product recommendations by climate zone.

**Property data teams** can append a reproducible hardiness attribute to US location records.

**Agricultural analysts** can add the USDA zone band as one input to broader location analysis.

**Data engineers** can convert a ZIP list into JSON, CSV, Excel, XML, or API output.

### Why use this ZIP enrichment workflow?

- Official 2023 USDA zone table rather than an inferred state-level zone
- Batch input instead of one interactive-map search at a time
- One stable row per unique supplied value
- Invalid and unmatched values remain visible as status rows
- Zone bounds in both Fahrenheit and Celsius
- Optional postal-centroid coordinates for joining and mapping
- Source provenance on every row
- Direct public JSON requests with a 256 MB memory profile
- Bounded retries for transient upstream failures

### What data can you extract?

| Field | Meaning |
| --- | --- |
| `inputZipCode` | Value exactly as supplied |
| `zipCode` | Normalized five-digit ZIP, or `null` for invalid input |
| `lookupStatus` | `found`, `not_found`, or `invalid` |
| `zone` | USDA zone and half-zone, such as `7b` |
| `zoneNumber` | Numeric zone, such as `7` |
| `subzone` | Five-degree half-zone, `a` or `b` |
| `minimumTemperatureF` | Lower temperature-band bound in °F |
| `maximumTemperatureF` | Upper temperature-band bound in °F |
| `minimumTemperatureC` | Lower temperature-band bound in °C |
| `maximumTemperatureC` | Upper temperature-band bound in °C |
| `temperatureBand` | Source-displayed USDA class label |
| `latitude` | Postal-centroid latitude when requested and found |
| `longitude` | Postal-centroid longitude when requested and found |
| `coordinateStatus` | `found`, `not_found`, or `skipped` |
| `coordinateType` | `postal_centroid` when coordinates are present |
| `matchedAddress` | Postal locality label from ArcGIS geocoding |
| `sourceEdition` | USDA map edition, currently `2023` |
| `sourceName` | Official source name |
| `sourceUrl` | USDA map page |
| `zoneDataUrl` | Public zone-table query endpoint |
| `coordinateSourceUrl` | Coordinate endpoint, or `null` when skipped |
| `retrievedAt` | ISO 8601 retrieval timestamp |

Fields unavailable for invalid, unmatched, skipped, or un-geocoded values are `null`.

### How to look up hardiness zones by ZIP code

1. Open the Actor input page.
2. Add one or more values under **US ZIP codes**.
3. Set **Maximum lookup rows** if you want to process only the first unique values.
4. Keep **Include coordinates** enabled when a postal centroid is useful.
5. Disable coordinates for faster zone-only enrichment.
6. Click **Start**.
7. Open the Dataset tab when the run finishes.
8. Export JSON, CSV, Excel, XML, or RSS, or consume the dataset through the API.

A useful first input is:

```json
{
  "zipCodes": ["19104", "33139", "80202", "98101"],
  "maxItems": 4,
  "includeCoordinates": true
}
```

### Input parameters

#### `zipCodes`

Required array containing one to 1,000 strings.

Accepted values are five digits or ZIP+4 format.

ZIP+4 values are normalized to the first five digits because the USDA source table is keyed by five-digit ZIP.

Malformed values produce an `invalid` row instead of disappearing.

Duplicate normalized ZIP codes are processed once.

#### `maxItems`

Optional maximum number of unique supplied values to process.

The default is 100.

The accepted range is 1 to 1,000.

Invalid inputs count toward the limit because they produce useful status rows.

#### `includeCoordinates`

Optional boolean, enabled by default.

When enabled, the Actor requests a representative postal centroid from the public ArcGIS World Geocoding service.

When disabled, latitude and longitude are `null` and `coordinateStatus` is `skipped`.

Coordinates represent a postal locality, not a parcel, street address, ZIP polygon, or exact USDA raster sample point.

### Output example

This record reflects the current output shape for Philadelphia ZIP code 19104:

```json
{
  "inputZipCode": "19104",
  "zipCode": "19104",
  "lookupStatus": "found",
  "zone": "7b",
  "zoneNumber": 7,
  "subzone": "b",
  "minimumTemperatureF": 5,
  "maximumTemperatureF": 10,
  "minimumTemperatureC": -15,
  "maximumTemperatureC": -12.2,
  "temperatureBand": "7b (5 to 10 °F/-15 to -12.2 °C)",
  "latitude": 39.9580995,
  "longitude": -75.2018556,
  "coordinateStatus": "found",
  "coordinateType": "postal_centroid",
  "matchedAddress": "19104, Philadelphia, Pennsylvania",
  "sourceEdition": "2023",
  "sourceName": "2023 USDA Plant Hardiness Zone Map",
  "sourceUrl": "https://planthardiness.ars.usda.gov/",
  "zoneDataUrl": "https://services1.arcgis.com/SyUSN23vOoYdfLC8/arcgis/rest/services/PHZM_2023_Zip_Code_Table/FeatureServer/296/query",
  "coordinateSourceUrl": "https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/findAddressCandidates",
  "retrievedAt": "2026-08-23T06:15:00.000Z"
}
```

### How much does it cost to look up USDA hardiness zones?

Pay-per-event billing has two parts:

- `$0.005` once when a run starts;
- one `item` event for each lookup row saved.

The current BRONZE item price is `$0.0023568` per saved row, with lower prices at higher usage tiers.

At the BRONZE rate, 10 rows cost about `$0.028568` including one start event.

At the BRONZE rate, 100 rows cost about `$0.24068` including one start event.

At the BRONZE rate, 1,000 rows cost about `$2.3618` including one start event.

Every saved row uses the same `item` event because each status outcome preserves one-to-one enrichment evidence for data-quality workflows.

Apify Console shows the authoritative live estimate before a run.

### Landscaping customer enrichment workflow

Export customer or lead ZIP codes from your CRM.

Run this Actor with the deduplicated or raw list.

Join output back to your source table using normalized `zipCode`.

Use `lookupStatus` to separate valid matches from records needing correction.

Use the zone as a segmentation field, then apply your own horticultural rules.

The Actor supplies climate-zone facts; it does not recommend plants or guarantee plant survival.

### Nursery catalog and mailing segmentation

Resolve customer destination ZIP codes in a scheduled task.

Export the dataset to a warehouse or spreadsheet.

Group orders or subscribers by `zone` and `subzone` downstream.

Combine that segment with species-specific requirements maintained by your team.

Keep `sourceEdition` and `retrievedAt` with the enriched record for auditability.

### Property portfolio enrichment

Supply ZIP codes from listings, parcels, facilities, or service territories.

Disable coordinates when you already have precise geometry.

Enable coordinates when a representative postal centroid helps with a lightweight map or join.

Do not treat the returned centroid as the location of a property.

### Scheduled comparisons

Create an Apify Task with a saved ZIP list.

Run it on a schedule appropriate for your pipeline.

Store finished datasets in your warehouse.

Compare `zone`, `temperatureBand`, and `sourceEdition` downstream.

The Actor does not itself send alerts or maintain cross-run change history.

### Integrations and exports

Send datasets to Google Sheets for operational review.

Export CSV or Excel for landscaping and nursery workflows.

Use webhooks to trigger Make, Zapier, or n8n after successful runs.

Load JSON into a warehouse for recurring location enrichment.

Use the API when ZIP codes originate in another application.

### Run with the Apify API

Replace `YOUR_TOKEN` with an Apify API token.

#### cURL

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/automation-lab~usda-hardiness-zone-zip-lookup/runs?token=YOUR_TOKEN&waitForFinish=120" \
  -H "Content-Type: application/json" \
  -d '{"zipCodes":["19104","33139"],"maxItems":2,"includeCoordinates":true}'
```

#### JavaScript

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client
  .actor('automation-lab/usda-hardiness-zone-zip-lookup')
  .call({ zipCodes: ['19104', '33139'], maxItems: 2, includeCoordinates: true });

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_TOKEN")
run = client.actor(
    "automation-lab/usda-hardiness-zone-zip-lookup"
).call(run_input={
    "zipCodes": ["19104", "33139"],
    "maxItems": 2,
    "includeCoordinates": True,
})

items = client.dataset(run["defaultDatasetId"]).list_items().items
print(items)
```

### Use with Apify MCP

Add the Actor to Claude Code:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/usda-hardiness-zone-zip-lookup"
```

#### Claude Desktop, Cursor, and VS Code setup

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/usda-hardiness-zone-zip-lookup"
    }
  }
}
```

Example prompts:

- "Look up the USDA hardiness zone for ZIP code 19104 and explain the temperature band."
- "Resolve these nursery customer ZIP codes and return a table of ZIP, zone, and postal centroid."
- "Enrich this property ZIP list and separate invalid or unmatched inputs."

### Accuracy, freshness, and provenance

Zone matches come from the public table used by the 2023 USDA Plant Hardiness Zone Map application.

The table assigns the majority zone associated with each ZIP code.

A ZIP can span more than one local climate or map cell.

For site-specific planting decisions, inspect the interactive USDA map and local conditions.

Coordinates come from ArcGIS World Geocoding and are representative postal points.

The Actor records the USDA edition and source URLs on every row.

Upstream tables, geocoding results, and service availability can change.

### Limits and failure behavior

The Actor supports supplied ZIP lookup, not address search, reverse geocoding, plant recommendations, or polygon export.

It processes at most 1,000 unique values per run.

Malformed inputs produce `invalid` rows.

A valid five-digit value absent from the USDA source produces `not_found`.

A found zone can have null coordinates when geocoding has no exact postal match.

Transient network failures, HTTP 429 responses, and server errors receive bounded retries.

A persistent upstream failure fails the run rather than returning misleading empty results.

No proxy or browser is required for the current public endpoints.

### Legality and responsible use

The Actor reads anonymous public government and ArcGIS data surfaces.

USDA plant hardiness zones are climate summaries, not safety, crop-yield, insurance, valuation, or agronomic guarantees.

Respect applicable source terms, Apify policies, and local requirements.

Do not use a postal centroid as a household, person, or precise property location.

### Troubleshooting

#### Why is `lookupStatus` `invalid`?

Use exactly five digits or ZIP+4 format such as `19104-1234`.

Keep ZIP codes as strings so leading zeroes are preserved.

#### Why is `lookupStatus` `not_found`?

The normalized five-digit value was not present in the current USDA ZIP table.

Check for typing errors and confirm that the value is a US ZIP code.

#### Why are coordinates null for a found zone?

Coordinate lookup may be disabled or the geocoder may not return an exact postal match.

Inspect `coordinateStatus` to distinguish `skipped` from `not_found`.

#### Why did the run fail instead of returning status rows?

Persistent service errors fail closed because fabricated or silently empty zone data would be unsafe.

Inspect the run log and retry later if the official service was temporarily unavailable.

### FAQ

#### Is this an official USDA product?

No. This is an independent Apify Actor that transforms public USDA map data into batch-friendly output. Source links are included for verification.

#### Does a ZIP code have only one microclimate?

No. The source ZIP table provides a majority-zone lookup. Elevation, terrain, water, urban heat, and local exposure can vary inside a ZIP.

#### Can I submit ZIP+4 codes?

Yes. They are normalized to five digits before lookup.

#### Are invalid and unmatched values saved?

Yes. Explicit status rows preserve the relationship between input and output for enrichment pipelines.

#### Can I disable geocoding?

Yes. Set `includeCoordinates` to `false` for faster zone-only processing.

#### Does the Actor monitor changes?

No. Schedule runs and compare datasets downstream if you need change detection.

### Related Automation Lab actors

Use [USDA Cash Grain Bids Scraper](https://apify.com/automation-lab/usda-cash-grain-bids-scraper) for public grain bid records rather than climate-zone lookup.

Use [USDA Organic Integrity Database Scraper](https://apify.com/automation-lab/usda-organic-integrity-database-scraper) for certified organic operation records.

These Actors cover different USDA workflows and do not replace this ZIP enrichment Actor.

# Actor input Schema

## `zipCodes` (type: `array`):

One to 1,000 five-digit US ZIP codes. ZIP+4 values are accepted and normalized to five digits. Duplicate ZIP codes are processed once.

## `maxItems` (type: `integer`):

Maximum number of unique supplied values to process. Invalid values count because they produce explicit status rows.

## `includeCoordinates` (type: `boolean`):

Look up representative postal-centroid coordinates with the public ArcGIS World Geocoding service. Disable for faster zone-only enrichment.

## Actor input object example

```json
{
  "zipCodes": [
    "19104",
    "33139",
    "80202",
    "98101"
  ],
  "maxItems": 20,
  "includeCoordinates": true
}
```

# Actor output Schema

## `overview` (type: `string`):

Open the normalized USDA plant hardiness zone lookup rows.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "zipCodes": [
        "19104",
        "33139",
        "80202",
        "98101"
    ],
    "maxItems": 20,
    "includeCoordinates": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/usda-hardiness-zone-zip-lookup").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "zipCodes": [
        "19104",
        "33139",
        "80202",
        "98101",
    ],
    "maxItems": 20,
    "includeCoordinates": True,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/usda-hardiness-zone-zip-lookup").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "zipCodes": [
    "19104",
    "33139",
    "80202",
    "98101"
  ],
  "maxItems": 20,
  "includeCoordinates": true
}' |
apify call automation-lab/usda-hardiness-zone-zip-lookup --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/usda-hardiness-zone-zip-lookup"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/NAedQxhhLFlJv8JAD/builds/boGZr6dgxo67atM7c/openapi.json
