You don’t need a Zillow developer account. You don’t need an npm package. You need fetch and an API key.

Here’s a complete Zillow property lookup in JavaScript. Five lines. Copy it, run it, and you’ll have Zestimates, tax records, and 300+ fields for any U.S. property in under 5 minutes.

const res = await fetch(
`https://api.zillapi.com/v1/properties/by-address?${new URLSearchParams({ address: "17 Zelma Dr, Greenville, SC 29617" })}`,
{ headers: { Authorization: `Bearer ${process.env.ZILLAPI_KEY}` } }
);
const { data } = await res.json();
console.log(`Zestimate: $${data.zestimate.toLocaleString()}`);
console.log(`Beds: ${data.bedrooms} | Baths: ${data.bathrooms} | Sqft: ${data.livingArea.toLocaleString()}`);

That’s it. The rest of this article shows you how to get your API key, what the response looks like, how to search for listings, how to add TypeScript types, and how to avoid the mistakes that trip up JavaScript developers building real estate integrations.

How do I set up the Zillow API for JavaScript?

Setting up Zillapi for JavaScript takes three steps: get a key, set an environment variable, and make your first call. The whole process takes under 2 minutes. No credit card. No MLS affiliation. No approval process. You get 100 free API credits at signup.

Step 1: Get your API key. Go to zillapi.com and sign up with your email. Click the magic link in your inbox. In the dashboard, create an API key. It looks like zk_AbCdEf.... Copy it immediately — it won’t show again.

Step 2: Set your key as an environment variable. Never hardcode API keys in your source files. Store them safely:

Terminal window
# Mac/Linux
export ZILLAPI_KEY="zk_your_key_here"
# Windows PowerShell
$env:ZILLAPI_KEY = "zk_your_key_here"
# .env file (for dotenv or Next.js)
ZILLAPI_KEY=zk_your_key_here

Step 3: Make your first call. Node.js 18+ has fetch built in. No npm install needed. If you’re on Node 16 or earlier, install node-fetch with npm install node-fetch.

That’s zero dependencies for modern Node.js. No SDK. No compiled packages. No Docker.

What does the API response look like?

When you call /v1/properties/by-address, you get back a JSON object with 300+ typed fields. Here’s what await res.json() returns for a real property:

{
data: {
zpid: "11026031",
address: {
streetAddress: "17 Zelma Dr",
city: "Greenville",
state: "SC",
zipcode: "29617"
},
price: 295000,
zestimate: 305100,
rentZestimate: 1850,
bedrooms: 3,
bathrooms: 2,
livingArea: 1432,
lotSize: 8712,
yearBuilt: 1965,
homeType: "SINGLE_FAMILY",
homeStatus: "NOT_LISTED",
taxAssessedValue: 187400,
schools: [...],
priceHistory: [...],
photos: [...]
}
}

Every field is typed. Numbers for prices. Strings for addresses. Arrays for schools, price history, and photos. No XML parsing. No HTML scraping. Just data.zestimate.

You can trim the response with the fields parameter. If you only need the Zestimate and basic specs:

const params = new URLSearchParams({
address: "17 Zelma Dr, Greenville, SC 29617",
fields: "zestimate,price,bedrooms,bathrooms,livingArea"
});
const res = await fetch(
`https://api.zillapi.com/v1/properties/by-address?${params}`,
{ headers: { Authorization: `Bearer ${process.env.ZILLAPI_KEY}` } }
);

That cuts the response from ~3,000 tokens to ~200 tokens. Useful when you’re feeding property data into an AI agent and want to save context window space.

How do I look up a property by Zillow URL or ZPID?

Zillapi offers three ways to look up a single property. Use whichever matches the data you already have.

By address (the example above):

const res = await fetch(
`https://api.zillapi.com/v1/properties/by-address?${new URLSearchParams({
address: "17 Zelma Dr, Greenville, SC 29617"
})}`,
{ headers: { Authorization: `Bearer ${process.env.ZILLAPI_KEY}` } }
);

By Zillow URL (when you’ve got a listing link):

const res = await fetch(
`https://api.zillapi.com/v1/properties/by-url?${new URLSearchParams({
url: "https://www.zillow.com/homedetails/17-Zelma-Dr-Greenville-SC-29617/11026031_zpid/"
})}`,
{ headers: { Authorization: `Bearer ${process.env.ZILLAPI_KEY}` } }
);

By ZPID (Zillow’s unique property ID — fastest if you already have it):

const res = await fetch(
"https://api.zillapi.com/v1/properties/11026031",
{ headers: { Authorization: `Bearer ${process.env.ZILLAPI_KEY}` } }
);

All three endpoints return the same 300+ field response. Each call costs 1 credit.

How do I search for listings?

Searching is different from looking up a single property. The search endpoint takes filters and returns multiple results.

const res = await fetch("https://api.zillapi.com/v1/search", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ZILLAPI_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
bbox: {
west: -82.45,
south: 34.80,
east: -82.35,
north: 34.90
},
minPrice: 200000,
maxPrice: 400000,
minBedrooms: 3,
homeType: ["SINGLE_FAMILY"]
})
});
const { data } = await res.json();
console.log(`Found ${data.length} properties`);
data.slice(0, 5).forEach(prop => {
console.log(` ${prop.address.streetAddress}: $${(prop.price ?? "N/A").toLocaleString()}`);
});

The bbox parameter is required. You define a bounding box with west, south, east, and north coordinates. Every listing within that rectangle matching your filters comes back.

One search call costs 1 credit regardless of how many results it returns.

How do I add TypeScript types?

If you’re using TypeScript, you want type safety on the API response. Here’s a starter interface covering the most-used fields:

interface ZillapiProperty {
zpid: string;
address: {
streetAddress: string;
city: string;
state: string;
zipcode: string;
};
price: number | null;
zestimate: number;
rentZestimate: number;
bedrooms: number;
bathrooms: number;
livingArea: number;
lotSize: number;
yearBuilt: number;
homeType: string;
homeStatus: string;
taxAssessedValue: number;
schools: Array<{
name: string;
rating: number;
distance: number;
type: string;
}>;
priceHistory: Array<{
date: string;
price: number;
event: string;
}>;
photos: string[];
}
interface ZillapiResponse {
data: ZillapiProperty;
}

Now your fetch calls get autocomplete and compile-time checks:

async function getProperty(address: string): Promise<ZillapiProperty> {
const params = new URLSearchParams({ address });
const res = await fetch(
`https://api.zillapi.com/v1/properties/by-address?${params}`,
{ headers: { Authorization: `Bearer ${process.env.ZILLAPI_KEY}` } }
);
if (!res.ok) {
throw new Error(`API error: ${res.status} ${res.statusText}`);
}
const json: ZillapiResponse = await res.json();
return json.data;
}
const property = await getProperty("17 Zelma Dr, Greenville, SC 29617");
console.log(property.zestimate); // TypeScript knows this is a number

The full API response has 300+ fields. Start with this interface and extend it as you use more fields. The API reference documents every field and its type.

How do I use this in Next.js?

Next.js is the most common framework JavaScript developers ask about. The key rule: never call the API from client-side code. Your API key would be visible in the browser’s network tab.

Use a Route Handler (App Router) or API Route (Pages Router) instead.

App Router (Next.js 13+):

app/api/property/route.ts
import { NextResponse } from "next/server";
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const address = searchParams.get("address");
if (!address) {
return NextResponse.json({ error: "Address required" }, { status: 400 });
}
const res = await fetch(
`https://api.zillapi.com/v1/properties/by-address?${new URLSearchParams({ address })}`,
{ headers: { Authorization: `Bearer ${process.env.ZILLAPI_KEY}` } }
);
const data = await res.json();
return NextResponse.json(data);
}

Pages Router:

pages/api/property.ts
import type { NextApiRequest, NextApiResponse } from "next";
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const { address } = req.query;
const apiRes = await fetch(
`https://api.zillapi.com/v1/properties/by-address?${new URLSearchParams({
address: address as string
})}`,
{ headers: { Authorization: `Bearer ${process.env.ZILLAPI_KEY}` } }
);
const data = await apiRes.json();
res.status(200).json(data);
}

Your frontend then calls /api/property?address=... instead of hitting Zillapi directly. The API key stays on the server. Your users never see it.

The same pattern works for Express, Fastify, Hono, or any server-side JavaScript framework. The principle is the same: API key on the server, proxy the request, return the data.

Why can’t I use node-zillow anymore?

The node-zillow npm package is dead. It relied on Zillow’s ZWSID API, which Zillow shut down on September 30, 2021. If you npm install node-zillow today and try to make a call, you’ll get a 403 error. The package hasn’t been updated since 2020 and Snyk flags it as inactive.

The same applies to zillow-node, zillow-api, and every other npm package built on the old ZWSID endpoints. They all hit dead URLs.

In 2026, the right approach is using the native fetch function with a REST API like Zillapi. No wrapper library needed. The API returns JSON. JavaScript handles JSON natively. Adding a dependency between you and the API just creates a point of failure that you have to maintain.

I’ve seen developers waste hours debugging why node-zillow returns errors before realizing the underlying API no longer exists. Save yourself the time. Use fetch directly.

What mistakes do JavaScript developers make with real estate APIs?

I’ve watched hundreds of developers integrate Zillapi into JavaScript projects. The same mistakes come up repeatedly.

The most dangerous one is exposing API keys in client-side code. If you put process.env.ZILLAPI_KEY in a React component or a browser script, that key ships to every user’s browser. Anyone can open DevTools, find the key in the network tab, and use your credits. Always proxy through a server-side route.

The second mistake is not checking response status. A 404 means the property wasn’t found. A 402 means you’re out of credits. A 429 means you hit the rate limit. Always check res.ok before calling res.json().

if (!res.ok) {
console.error(`Error ${res.status}: ${await res.text()}`);
return;
}
const { data } = await res.json();

Third is running parallel requests without respecting rate limits. JavaScript makes concurrency easy with Promise.all, but firing 500 requests simultaneously will hit the 200 requests per minute limit. Use batching:

async function batchLookup(addresses, batchSize = 50) {
const results = [];
for (let i = 0; i < addresses.length; i += batchSize) {
const batch = addresses.slice(i, i + batchSize);
const promises = batch.map(addr =>
fetch(
`https://api.zillapi.com/v1/properties/by-address?${new URLSearchParams({ address: addr })}`,
{ headers: { Authorization: `Bearer ${process.env.ZILLAPI_KEY}` } }
).then(r => r.json())
);
results.push(...await Promise.all(promises));
// Pause between batches to stay under rate limit
if (i + batchSize < addresses.length) {
await new Promise(resolve => setTimeout(resolve, 15000));
}
}
return results;
}

Then there’s ignoring the fields parameter. If you only need 5 fields, don’t pull all 300+. Use fields=zestimate,price,bedrooms,bathrooms,livingArea. That cuts response size by 90% and speeds up your application.

Finally, most developers skip error handling for network failures. Wrap API calls in try-catch and add retry logic for transient errors:

async function fetchWithRetry(url, options, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
const res = await fetch(url, options);
if (res.ok) return res;
if (res.status >= 400 && res.status < 500) return res; // Don't retry client errors
} catch (err) {
if (i === retries - 1) throw err;
}
await new Promise(r => setTimeout(r, 1000 * Math.pow(2, i)));
}
}

Can I use this with Deno or Bun?

Yes. Both Deno and Bun have built-in fetch and support the same code shown throughout this article. No modifications needed.

In Deno, set your API key with:

Terminal window
export ZILLAPI_KEY="zk_your_key_here"
deno run --allow-net --allow-env your-script.ts

In Bun, the code runs exactly as written:

Terminal window
ZILLAPI_KEY="zk_your_key_here" bun run your-script.ts

The Zillapi API is a standard REST endpoint. Any JavaScript runtime with fetch support works. That includes Node.js 18+, Deno, Bun, Cloudflare Workers, and Vercel Edge Functions.

Get your API key and start building

Go to zillapi.com. Sign up. Get 100 free credits.

Then open your terminal, paste the 5-line script from the top of this article, and watch real property data come back. You’ll have a working JavaScript integration before your coffee gets cold.

For the Python version of this tutorial, check out our Zillow API Python guide. For pricing details across all providers, see the full cost breakdown.

Frequently asked questions

How do I use the Zillow API with JavaScript?

Use the native fetch function with a Zillapi API key. Sign up at zillapi.com to get 100 free credits with no credit card. Call the /v1/properties/by-address endpoint with your key in the Authorization header. The response is JSON with 300+ fields including Zestimates, tax records, price history, and school ratings. Five lines of JavaScript is all you need for a working property lookup.

Is there a Node.js library for the Zillow API?

The old node-zillow npm package used Zillow’s ZWSID API, which was retired in September 2021. That package is inactive and no longer works. In 2026, the best approach is using the native fetch function (built into Node.js 18+) with a REST API like Zillapi. No npm package needed. The API returns JSON that works directly with response.json().

Can I get Zestimate data with JavaScript?

Yes. Zillapi’s /v1/properties/by-address endpoint returns the zestimate field as an integer in dollars for any U.S. property. Call it with fetch and read the value from the JSON response at data.zestimate. The rent Zestimate is available as data.rentZestimate. Both update regularly and match the values shown on Zillow.com property pages.

How do I use the Zillow API with TypeScript?

Define an interface for the API response with typed fields like zestimate as number, bedrooms as number, and address as a nested object. Then use fetch with the same endpoint and headers as plain JavaScript. TypeScript gives you autocomplete and compile-time checks on every property field. A complete type definition covers the 300+ fields in the response.

Is the Zillow API free for JavaScript developers?

Zillapi offers 100 free API credits at signup with no credit card required. Each successful API call uses 1 credit regardless of which endpoint you hit or how much data comes back. That is enough to build and test a complete JavaScript integration. After the free credits, paid plans start at $5 per month for 1,000 credits.

Can I call the Zillow API from a browser?

You should not call the API directly from browser-side JavaScript because that exposes your API key in the client bundle. Instead, create a server-side route in Next.js, Express, or any backend framework that proxies the request. Your frontend calls your own API route, and your server calls Zillapi with the key stored in an environment variable. This keeps your key safe.