API Documentation
Integrate Gadsden Valuations into your applications. Request property valuations, run batch jobs, and manage your account programmatically.
https://gadsdenvaluations.com/api
Version: v1
Format: JSON
Authentication
All API requests require a Bearer token in the Authorization header. API keys are prefixed with gak_ and can be created from your dashboard or via the API.
Authorization: Bearer gak_your_api_key_here
Getting your API key
- Create an account at gadsdenvaluations.com
- Buy any bundle of 10 or more valuations — API access is enabled automatically. Or contact us to request it.
- Create an API key in the API Access section of your dashboard — store it securely, it is only shown once
You can have up to 5 active API keys per account. Keys can be revoked at any time and take effect immediately.
Quick Start
Request your first valuation in under a minute:
curl -X POST https://gadsdenvaluations.com/api/v1/valuations \
-H "Authorization: Bearer gak_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"postcode": "M20 2TG", "address": "Dene Road"}'
The response includes a point estimate, FSD-based confidence band, valuation range, comparable evidence, and the features that drove the valuation.
Rate Limits
API requests are rate-limited per account using a sliding window. The default limit is 60 requests per minute. When the limit is exceeded, the API returns 429 Too Many Requests:
{
"error": "Rate limit exceeded",
"limit": 60,
"retry_after": 60
}
Wait for the retry_after period (in seconds) before making additional requests. Contact us if you need a higher rate limit for production integrations.
POST /v1/valuations
Request a valuation for a single property. Identify the property by UPRN, postcode + address, or internal property ID. Consumes 1 valuation from your balance on success. No charge for no-value responses.
Request body
| Parameter | Type | Required | Description |
|---|---|---|---|
uprn | integer | * | Unique Property Reference Number. Preferred identifier — unambiguous. |
postcode | string | * | UK postcode (e.g. "M20 2TG"). Use with address for best results. |
address | string | No | Partial address to disambiguate when using postcode. Matched with ILIKE. |
property_id | integer | * | Internal Gadsden property ID (from a previous valuation response). |
* At least one of uprn, postcode, or property_id is required.
Success response (200)
{
"valuation_id": 1042,
"status": "success",
"property_id": 58291,
"uprn": "100012345678",
"predicted_value": 347500,
"valuation_range": {
"low": 301700,
"high": 393300
},
"confidence": {
"level": 2,
"fsd": 0.08,
"fsd_band": "A",
"fitch_haircut": 0.025,
"fitch_haircut_pct": 2.5
},
"property": {
"address": "42 Dene Road, Didsbury",
"type": "Semi-Detached",
"postcode": "M20 2TG",
"bedrooms": 3,
"floor_area_sqm": 95.0,
"construction_period": "1919-1945",
"epc_rating": "D",
"last_sale": {
"date": "2019-06-14",
"price": 285000
}
},
"comparables": [
{
"address": "38 Dene Road, Didsbury",
"distance_m": 42,
"sale_date": "2025-11-20",
"sale_price": 335000,
"property_type": "S",
"floor_area_sqm": 88.0
}
],
"feature_contributions": [
{"feature": "floor_area_sqm", "contribution": 1842.5},
{"feature": "latitude", "contribution": 1203.1},
{"feature": "hpi_adjusted_price", "contribution": 987.3}
],
"model_version": "v6",
"processing_time_ms": 215
}
No-value response (200)
{
"valuation_id": 1043,
"status": "no_value",
"valuation": null,
"reason": "insufficient_comparables",
"reason_description": "Fewer than 3 comparable sales within the search radius and time window",
"missing_data": ["recent_comparables"],
"property_id": 72014,
"uprn": "200098765432",
"model_version": "v6",
"processing_time_ms": 87
}
GET /v1/valuations/{id}
Retrieve a previously completed valuation by its ID. Only returns valuations belonging to your account.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | integer | The valuation_id from a previous valuation response. |
Response (200)
{
"valuation_id": 1042,
"property_id": 58291,
"uprn": "100012345678",
"address": "42 Dene Road, Didsbury",
"postcode": "M20 2TG",
"predicted_value": 347500,
"confidence_level": 2,
"fsd_band": "A",
"status": "complete",
"created_at": "2026-03-08T14:22:10.000000Z",
"completed_at": "2026-03-08T14:22:10.000000Z"
}
POST /v1/valuations/batch
Request valuations for up to 100 properties in a single call. Each successful result consumes 1 valuation from your balance. No-value results and lookup failures are not charged.
Request body
| Parameter | Type | Required | Description |
|---|---|---|---|
properties | array | Yes | Array of 1–100 property objects, each with the same fields as the single valuation endpoint. |
Request example
{
"properties": [
{"uprn": 100012345678},
{"postcode": "SW1A 1AA", "address": "Downing Street"},
{"property_id": 58291}
]
}
Response (200)
{
"results": [
{
"property_id": 58291,
"predicted_value": 347500,
"confidence": {
"level": 2,
"fsd": 0.08,
"fsd_band": "A",
"fitch_haircut": 0.025,
"fitch_haircut_pct": 2.5
},
"status": "success"
},
{
"property_id": 72014,
"status": "no_value",
"reason": "insufficient_comparables",
"reason_description": "Fewer than 3 comparable sales within the search radius and time window",
"missing_data": ["recent_comparables"]
},
{
"input": {"postcode": "INVALID"},
"error": "Property not found"
}
],
"total": 3
}
The batch endpoint pre-checks your valuation balance against the total number of properties. If your balance is lower than the batch size, the request is rejected with a 402 before any valuations are attempted.
Individual failures (property not found, no-value, AVM errors) do not halt the batch — remaining properties continue processing. However, if your balance runs out mid-batch, processing stops.
GET /v1/account
Retrieve your account details including current valuation balance and rate limit.
Response (200)
{
"user_id": 12,
"email": "developer@lender.co.uk",
"plan_type": "standard",
"valuation_balance": 487,
"api_enabled": true,
"api_rate_limit": 60
}
If your account belongs to an organisation, valuation_balance reflects the shared organisation pool.
GET /v1/keys
List all API keys associated with your account, including revoked keys.
Response (200)
{
"keys": [
{
"id": 1,
"key_prefix": "gak_Hp8z0eK1",
"name": "Production",
"last_used_at": "2026-03-08T12:30:00.000000Z",
"created_at": "2026-01-15T10:00:00.000000Z",
"revoked_at": null
},
{
"id": 2,
"key_prefix": "gak_Qm4xWr7p",
"name": "Staging",
"last_used_at": null,
"created_at": "2026-02-20T09:15:00.000000Z",
"revoked_at": "2026-03-01T14:00:00.000000Z"
}
]
}
POST /v1/keys
Create a new API key. Maximum 5 active keys per account. The full key is only returned once — store it securely.
Request body
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | No | A label for this key (max 100 characters). E.g. "Production", "Staging". |
Response (201)
{
"id": 3,
"key": "gak_Hp8z0eK1q7X9pL2m4R6sT8uV0wY2zA4bCdEfGhIjKl",
"key_prefix": "gak_Hp8z0eK1",
"name": "Production",
"message": "Store this key securely. It will not be shown again."
}
DELETE /v1/keys/{id}
Revoke an API key. Takes effect immediately — any in-flight requests using this key will fail.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | integer | The key ID (from the list keys response). |
Response (200)
{
"message": "API key revoked"
}
Response Fields Reference
Complete field reference for the valuation response object.
| Field | Type | Description |
|---|---|---|
valuation_id | integer | Unique identifier for this valuation request. Use to retrieve later via GET /v1/valuations/:id. |
status | string | "success" or "no_value". A success means the model produced a valuation. A no-value means the property could not be reliably valued. |
property_id | integer | Internal Gadsden property ID. Stable across requests — can be reused for future valuations. |
uprn | string | Unique Property Reference Number (Ordnance Survey). |
predicted_value | integer | Point estimate of market value in GBP. Only present when status is "success". |
valuation_range
| Field | Type | Description |
|---|---|---|
low | integer | Lower bound of the 90% prediction interval (GBP). Calculated as predicted_value × (1 − 1.645 × FSD). |
high | integer | Upper bound of the 90% prediction interval (GBP). Calculated as predicted_value × (1 + 1.645 × FSD). |
confidence
| Field | Type | Description |
|---|---|---|
level | integer | Underlying confidence level score from the model. |
fsd | float | Forecast Standard Deviation. The model's estimate of its own prediction error for this property. Drives both the valuation range and the classification band. |
fsd_band | string | Fitch/EAA classification band derived from FSD: "A" (FSD ≤ 0.05), "B" (≤ 0.10), "C" (≤ 0.20), or "D" (> 0.20). |
fitch_haircut | float | The haircut fraction Fitch’s published RMBS criteria associate with the band (e.g. 0.025 = 2.5%). Provided for reference to Fitch’s framework only — not a Gadsden Valuations recommendation. |
fitch_haircut_pct | float | The same Fitch reference figure as a percentage (e.g. 2.5). |
property
| Field | Type | Description |
|---|---|---|
address | string | Full property address. |
type | string | Property type (e.g. Detached, Semi-Detached, Terraced, Flat, Maisonette, Bungalow). |
postcode | string | UK postcode. |
bedrooms | integer | Number of bedrooms (may be model-predicted if not in source data). |
floor_area_sqm | float | Total floor area in square metres (from EPC data where available). |
construction_period | string | Construction age band (e.g. "1919-1945", "2007-onwards"). |
epc_rating | string | Current EPC rating (A–G). |
last_sale.date | string | Date of most recent Land Registry transaction (ISO 8601). |
last_sale.price | integer | Price paid at last transaction (GBP). |
comparables
Array of up to 5 comparable property sales used as market evidence. Each comparable includes:
| Field | Type | Description |
|---|---|---|
address | string | Comparable property address. |
distance_m | integer | Distance from subject property in metres. |
sale_date | string | Date of sale (ISO 8601). |
sale_price | integer | Transaction price (GBP). |
property_type | string | Land Registry type code: D (Detached), S (Semi), T (Terraced), F (Flat). |
floor_area_sqm | float | Floor area of the comparable (where available). |
feature_contributions
Array of up to 10 features showing what most influenced the valuation. Each entry has:
| Field | Type | Description |
|---|---|---|
feature | string | Feature name (e.g. floor_area_sqm, latitude, hpi_adjusted_price). |
contribution | float | Feature importance score (LightGBM gain). Higher values indicate greater influence on the valuation. |
Confidence Scoring & FSD
Every valuation carries a per-property Forecast Standard Deviation (FSD) — the model's estimate of its own prediction error. FSD is the European AVM Alliance metric used by Fitch and S&P for RMBS analysis and is the sole confidence expression in the API response.
FSD is derived from a granular lookup (decile × property type × price band) calibrated against realised Land Registry sales, and is mapped to a Fitch RMBS classification band:
| Band | FSD range |
|---|---|
| A | ≤ 0.05 |
| B | 0.05 – 0.10 |
| C | 0.10 – 0.20 |
| D | > 0.20 |
The valuation_range (low/high) is derived directly from FSD: predicted_value × (1 ± 1.645 × FSD), giving a 90% prediction interval.
For lender integration, fsd_band lets you apply your own risk policy — for example, accepting Bands A and B for low-LTV lending and escalating Band D for physical survey.
No-Value Responses
The API will decline to value a property when there is insufficient data for a reliable estimate. No-value responses return status: "no_value" with HTTP 200 and are not charged.
Reason codes
| Reason | Description |
|---|---|
missing_critical_data | Floor area, bedrooms, and property type are all unknown. The model cannot produce a meaningful estimate without at least one physical characteristic. |
insufficient_comparables | Fewer than 3 comparable sales found within the search radius (2km) and time window (5 years). Typical for very rural or unusual properties. |
The missing_data array provides additional detail about what data was absent, helping you determine whether a no-value is resolvable (e.g. by supplying a UPRN instead of an approximate postcode match).
See our No-Value Policy for the full methodology behind valuation refusal.
Error Handling
The API uses standard HTTP status codes. Error responses include a JSON body with an error field.
| Status | Meaning | Example error message |
|---|---|---|
400 | Bad request — missing required parameters or invalid input | "property_id, uprn, or postcode+address required" |
401 | Authentication failed — missing, invalid, or revoked API key | "Missing or invalid Authorization header" |
402 | Insufficient valuations to complete the request | "Insufficient valuations" |
403 | API access disabled on your account | "API access disabled" |
404 | Property not found in our database | "Property not found" |
422 | Validation error — request body failed validation rules | Laravel validation error object |
429 | Rate limit exceeded | "Rate limit exceeded" with retry_after: 60 |
503 | AVM service temporarily unavailable | "AVM service unavailable" |
Error response format
{
"error": "Insufficient valuations"
}
For 429 responses, the body also includes limit and retry_after fields:
{
"error": "Rate limit exceeded",
"limit": 60,
"retry_after": 60
}
Billing
Valuations are pay-per-use. No subscription required — purchased valuations never expire.
- 1 valuation deducted per successful request (single or batch)
- No charge for no-value responses, property-not-found errors, or failed lookups
- Batch pre-check: the batch endpoint checks your balance against the total batch size before starting. If insufficient, the entire batch is rejected with
402. - Organisation accounts: valuations are pooled across the organisation. Any member can draw from the shared balance.
Purchase valuations from the pricing page or your dashboard. Check your current balance programmatically with GET /v1/account.
Testing
There are two ways to evaluate the API without committing to production volumes:
Free backtesting
Upload a CSV of properties where you already know the sale price. Our backtest tool will run valuations against your data and produce a comparison report showing accuracy metrics (MdAPE, PE10, PE15, PE20) broken down by property type, price band, and region. Backtesting is free — no credit card required.
This is the recommended approach for lender evaluation. Test our model against your own portfolio data and take the comparison report to your risk committee.
Single valuations
Purchase a small valuation bundle and make individual API calls. The smallest bundle starts at £9.95. No-value responses are free, so you can test address resolution and property lookup without being charged.
Code Examples
cURL
# Single valuation by UPRN
curl -X POST https://gadsdenvaluations.com/api/v1/valuations \
-H "Authorization: Bearer gak_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"uprn": 100012345678}'
# Single valuation by postcode + address
curl -X POST https://gadsdenvaluations.com/api/v1/valuations \
-H "Authorization: Bearer gak_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"postcode": "M20 2TG", "address": "Dene Road"}'
# Batch valuation
curl -X POST https://gadsdenvaluations.com/api/v1/valuations/batch \
-H "Authorization: Bearer gak_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"properties": [{"uprn": 100012345678}, {"postcode": "SW1A 1AA"}]}'
# Check valuation balance
curl https://gadsdenvaluations.com/api/v1/account \
-H "Authorization: Bearer gak_your_api_key_here"
Python
import requests
API_KEY = "gak_your_api_key_here"
BASE_URL = "https://gadsdenvaluations.com/api/v1"
HEADERS = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
}
# Single valuation
response = requests.post(
f"{BASE_URL}/valuations",
headers=HEADERS,
json={"postcode": "M20 2TG", "address": "Dene Road"},
)
data = response.json()
if data["status"] == "success":
print(f"Value: £{data['predicted_value']:,}")
print(f"Range: £{data['valuation_range']['low']:,} – £{data['valuation_range']['high']:,}")
print(f"FSD band: {data['confidence']['fsd_band']} (FSD {data['confidence']['fsd']})")
elif data["status"] == "no_value":
print(f"No value: {data['reason_description']}")
# Batch valuation
batch_response = requests.post(
f"{BASE_URL}/valuations/batch",
headers=HEADERS,
json={
"properties": [
{"uprn": 100012345678},
{"postcode": "SW1A 1AA"},
{"postcode": "M1 1AA", "address": "Piccadilly"},
]
},
)
for result in batch_response.json()["results"]:
if result.get("status") == "success":
print(f"Property {result['property_id']}: £{result['predicted_value']:,}")
elif result.get("status") == "no_value":
print(f"Property {result['property_id']}: no value ({result['reason']})")
else:
print(f"Error: {result.get('error')}")
# Check valuation balance
account = requests.get(f"{BASE_URL}/account", headers=HEADERS).json()
print(f"Valuations remaining: {account['valuation_balance']}")
Node.js
const API_KEY = "gak_your_api_key_here";
const BASE_URL = "https://gadsdenvaluations.com/api/v1";
async function valuate(postcode, address) {
const response = await fetch(`${BASE_URL}/valuations`, {
method: "POST",
headers: {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ postcode, address }),
});
if (!response.ok) {
const err = await response.json();
throw new Error(`${response.status}: ${err.error}`);
}
return response.json();
}
// Single valuation
const data = await valuate("M20 2TG", "Dene Road");
if (data.status === "success") {
console.log(`Value: £${data.predicted_value.toLocaleString()}`);
console.log(`FSD band: ${data.confidence.fsd_band} (FSD ${data.confidence.fsd})`);
} else {
console.log(`No value: ${data.reason_description}`);
}
// Check valuation balance
const account = await fetch(`${BASE_URL}/account`, {
headers: { Authorization: `Bearer ${API_KEY}` },
}).then((r) => r.json());
console.log(`Valuations: ${account.valuation_balance}`);
PHP (Guzzle)
use GuzzleHttp\Client;
$client = new Client([
'base_uri' => 'https://gadsdenvaluations.com/api/v1/',
'headers' => [
'Authorization' => 'Bearer gak_your_api_key_here',
'Content-Type' => 'application/json',
],
]);
// Single valuation
$response = $client->post('valuations', [
'json' => ['postcode' => 'M20 2TG', 'address' => 'Dene Road'],
]);
$data = json_decode($response->getBody(), true);
if ($data['status'] === 'success') {
echo "Value: £" . number_format($data['predicted_value']) . "\n";
echo "Range: £" . number_format($data['valuation_range']['low'])
. " – £" . number_format($data['valuation_range']['high']) . "\n";
echo "FSD band: {$data['confidence']['fsd_band']} (FSD {$data['confidence']['fsd']})\n";
} elseif ($data['status'] === 'no_value') {
echo "No value: {$data['reason_description']}\n";
}
// Batch valuation
$batchResponse = $client->post('valuations/batch', [
'json' => [
'properties' => [
['uprn' => 100012345678],
['postcode' => 'SW1A 1AA'],
],
],
]);
$batch = json_decode($batchResponse->getBody(), true);
foreach ($batch['results'] as $result) {
if (($result['status'] ?? null) === 'success') {
echo "Property {$result['property_id']}: £" . number_format($result['predicted_value']) . "\n";
}
}
// Check balance
$account = json_decode($client->get('account')->getBody(), true);
echo "Valuations: {$account['valuation_balance']}\n";
OpenAPI Specification
A machine-readable OpenAPI 3.0 specification is available for code generation and API client tooling.
curl https://gadsdenvaluations.com/api/v1/openapi.json | python -m json.tool
Use this to auto-generate client libraries with tools like OpenAPI Generator, or import directly into Postman.
Further Reading
- Audit Trail & Data Governance — how every valuation is recorded, stored, and made reproducible
- For Lenders — integration guide for mortgage lenders and model risk teams
- Accuracy Metrics — current backtest results and segmented performance