Zillow API in C# (.NET)
Look up Zillow property and listing data from C# with HttpClient and System.Text.Json, using nullable types so a missing price never turns into zero.
Base URL https://api.propertiesapi.com/v1 · auth header x-api-key. PropertiesAPI is an independent service that returns Zillow property and listing data as JSON, provided as-is.
How C# fits
System.Text.Json maps a JSON null onto a nullable property, so declaring price, zestimate and rentZestimate as decimal? keeps the difference between 'not shown' and zero all the way into your UI or database.
Create one HttpClient for the app and raise its Timeout: the default is long enough for most lookups but a live lookup takes typically 8 seconds, up to about 20 seconds and batches take longer. In ASP.NET Core, register it with IHttpClientFactory and keep the key in user secrets or your vault.
The response property names are camelCase, which matches System.Text.Json's web defaults, so the records below need no attributes except for the calculated fields, which use snake_case.
Records for the response
Nullable types for every value that can be missing.
using System.Net.Http.Json;
using System.Text.Json;
using System.Text.Json.Serialization;
public record Calculated(
[property: JsonPropertyName("gross_yield_pct")] decimal? GrossYieldPct,
[property: JsonPropertyName("price_vs_zestimate_pct")] decimal? PriceVsZestimatePct,
[property: JsonPropertyName("days_on_market")] int? DaysOnMarket);
public record Home(string? Zpid, string DetailUrl, string Address, string? HomeStatus,
decimal? Price, decimal? Zestimate, decimal? RentZestimate,
decimal? Beds, decimal? Baths, decimal? LivingArea, Calculated Calculated);
public record Meta(int Credits, bool Cached);
public record PropertyResponse(Home Data, Meta Meta);
public record ApiError(string Code, string Message, JsonElement? Details);
public record ErrorBody(ApiError Error);
public record BatchItem(int Index, bool Ok, Home? Data, ApiError? Error);
public record BatchResponse(List<BatchItem> Data);
public class PropertiesApiException(int status, ApiError error) : Exception($"{status} {error.Code}: {error.Message}")
{
public int Status { get; } = status;
public ApiError Error { get; } = error;
}A small client
One HttpClient, the key in a default header, and errors turned into exceptions.
public class PropertiesApi
{
static readonly JsonSerializerOptions Json = new(JsonSerializerDefaults.Web);
readonly HttpClient _http = new() { BaseAddress = new Uri("https://api.propertiesapi.com/v1/"), Timeout = TimeSpan.FromSeconds(320) };
public PropertiesApi(string apiKey) => _http.DefaultRequestHeaders.Add("x-api-key", apiKey);
public async Task<PropertyResponse> GetPropertyAsync(string param, string value, CancellationToken ct = default)
{
var res = await _http.GetAsync($"zillow/property?{param}={Uri.EscapeDataString(value)}", ct);
return await Read<PropertyResponse>(res, ct);
}
public async Task<BatchResponse> BatchAsync(IEnumerable<string> items, CancellationToken ct = default)
{
var res = await _http.PostAsJsonAsync("zillow/batch", new { items }, ct);
return await Read<BatchResponse>(res, ct);
}
static async Task<T> Read<T>(HttpResponseMessage res, CancellationToken ct)
{
if (!res.IsSuccessStatusCode)
{
var err = await res.Content.ReadFromJsonAsync<ErrorBody>(Json, ct);
throw new PropertiesApiException((int)res.StatusCode, err?.Error ?? new ApiError("unknown", "request failed", null));
}
return (await res.Content.ReadFromJsonAsync<T>(Json, ct))!;
}
}Look up a home and handle errors
Branch on the error code: not_found and address_ambiguous need a person, rate limits and busy answers need a wait.
var api = new PropertiesApi(Environment.GetEnvironmentVariable("PROPERTIESAPI_KEY")!);
try
{
var r = await api.GetPropertyAsync("address", "2640 Steiner St, San Francisco, CA 94115");
Console.WriteLine($"{r.Data.Address} {r.Data.HomeStatus}");
Console.WriteLine(r.Data.Price is decimal p ? $"List price ${p:N0}" : "No list price shown");
Console.WriteLine(r.Data.Zestimate is null ? "No Zestimate shown" : $"Zestimate ${r.Data.Zestimate:N0}");
Console.WriteLine(r.Meta.Cached ? "cache hit (free)" : $"live lookup, {r.Meta.Credits} credit");
}
catch (PropertiesApiException e) when (e.Error.Code == "not_found")
{
Console.WriteLine("No matching Zillow property (not billed)");
}
catch (PropertiesApiException e) when (e.Error.Code == "address_ambiguous")
{
Console.WriteLine($"Closest match: {e.Error.Details?.GetProperty("candidate").GetProperty("detailUrl")}");
}Batch a list
Up to 50 items per request; check Ok on each result.
var batch = await api.BatchAsync(new[] { "https://www.zillow.com/homedetails/2640-Steiner-St-San-Francisco-CA-94115/15073305_zpid/", "742 Evergreen Ter, Springfield, IL 62704" });
foreach (var item in batch.Data)
Console.WriteLine(item.Ok ? $"{item.Index}: {item.Data!.Address} {item.Data.Price}" : $"{item.Index}: {item.Error!.Code}");C# gotchas
- HttpClient.Timeout applies to the whole client. The example sets it for batches; pass a CancellationToken with a shorter deadline for single lookups if you want them to give up sooner.
- Don't create a new HttpClient per request in a loop. Reuse one, or use IHttpClientFactory in ASP.NET Core.
- decimal? fields print as an empty string when null. Decide in your UI what a missing Zestimate should say.
- Set a client timeout of at least 120 seconds: an occasional lookup takes longer.
Zillow API and C#: questions
Is there a Zillow API NuGet package?+
Packages written for Zillow's retired ZWSID API no longer work. With PropertiesAPI, HttpClient and System.Text.Json are enough.
Can I call it from Blazor WebAssembly?+
Not directly: code in the browser would expose your key. Call the API from your ASP.NET Core backend and send the result to the client.
Does it work in Azure Functions or AWS Lambda?+
Yes, as long as the function's own timeout allows for a live lookup. Batches need a longer function timeout than single lookups.
What does a lookup cost?+
Zillow links and addresses cost the same: 1 credit per successful lookup. Repeat lookups of the same home within 24 hours come from our cache and are free. You start with 50 free lookups.
Make your first call from C#
50 free lookups, no card, no approval.
Zillow is a trademark of Zillow, Inc. PropertiesAPI is an independent service and is not affiliated with, endorsed by or sponsored by Zillow.