LandPortal API Documentation

Comprehensive API reference for all LandPortal services

Download PDF

Skip Trace API

The Skip Trace API allows you to create skip trace tasks and retrieve their status. All requests must include a valid JWT token in the Authorization header.

Base URL:
https://landportal.com/wp-json/lp-rest-api/v1

Authentication

All requests must include a JWT token:

Authorization: Bearer <JWT_TOKEN>

1. Create Task

Endpoint: POST /skip-trace
Headers:
Content-Type: application/json
Authorization: Bearer <JWT_TOKEN>
Required Conditions:
  • Active subscription
  • Sufficient tokens
  • Either input_file or json_data must be provided
  • When using input_file, a fields mapping must be included

Option 1: File via URL

{
  "method": "create",
  "input_file": "https://example.com/data.csv",
  "has_header": true,
  "fields": {
    "last_name": "last_name",
    "first_name": "first_name",
    "mailing_address": "mailing_address",
    "mailing_city": "mailing_city",
    "mailing_state": "mailing_state",
    "mailing_zip": "mailing_zip",
    "property_address": "property_address",
    "property_city": "property_city",
    "property_state": "property_state",
    "property_zip": "property_zip"
  },
  "emails_trace": false,
  "demographics": false,
  "test_mode": "true"
}

Option 2: Inline JSON Data

{
  "method": "create",
  "json_data": [
    {
      "last_name": "Smith",
      "first_name": "John",
      "mailing_address": "123 Main St",
      "mailing_city": "New York",
      "mailing_state": "NY",
      "mailing_zip": "10001",
      "property_address": "456 Oak Ave",
      "property_city": "Brooklyn",
      "property_state": "NY",
      "property_zip": "11201"
    }
  ],
  "emails_trace": false,
  "demographics": false,
  "test_mode": "true"
}

Success Response

{
  "success": true,
  "message": "Skip trace created successfully",
  "data": {
    "data_length": 100,
    "input_file": "https://s3.amazonaws.com/bucket/skip-trace-files/uuid_userid_timestamp.csv",
    "task_id": 12345,
    "tokens_left": 950
  }
}

Test Mode Parameter

test_mode: Optional parameter that can be set to "true" for testing purposes.
Test Mode Behavior:
  • Fake Data: Output file will be populated with fake/sample data instead of real results
  • No Token Usage: Tokens are not consumed in test mode
  • Development Testing: Perfect for testing your integration without using real data or tokens
  • Response Format: The response structure remains the same

Examples

cURL
curl -X POST "https://landportal.com/wp-json/lp-rest-api/v1/skip-trace" \
  -H "Authorization: Bearer <JWT_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "method": "create",
    "json_data": [{"last_name":"Smith","first_name":"John"}],
    "test_mode": "true"
  }'
Python (requests)
import requests

url = "https://landportal.com/wp-json/lp-rest-api/v1/skip-trace"
headers = {"Authorization": "Bearer <JWT_TOKEN>"}
payload = {
    "method": "create",
    "json_data": [{"last_name": "Smith", "first_name": "John"}],
    "test_mode": "true"
}

response = requests.post(url, json=payload, headers=headers)
print(response.json())
JavaScript (fetch)
const response = await fetch("https://landportal.com/wp-json/lp-rest-api/v1/skip-trace", {
  method: "POST",
  headers: {
    "Authorization": "Bearer <JWT_TOKEN>",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    method: "create",
    json_data: [{ last_name: "Smith", first_name: "John" }],
    test_mode: "true"
  })
});

const data = await response.json();
console.log(data);

2. Get Task Status

Endpoint: POST /skip-trace

Request Body

{
  "method": "get",
  "task_id": 12345
}

Success Response

{
  "success": true,
  "message": "Skip trace data",
  "data": {
    "task_id": 12345,
    "task_status": "completed",
    "total_rows": 100,
    "success_rows": 95,
    "output_file_csv": "https://skip-trace.landportal.com/files/uuid_file.csv"
  }
}

3. Stop Task

Endpoint: POST /skip-trace
Headers:
Content-Type: application/json
Authorization: Bearer <JWT_TOKEN>
Required Parameters:
  • method — Must be "stop"
  • task_id — The ID of the task to stop
Note: Once stopped, the task cannot be resumed and any partial results will be lost.

Request Body

{
  "method": "stop",
  "task_id": 12345
}

Success Response

{
  "success": true,
  "message": "Skip trace task stopped successfully",
  "data": { "response_code": 200 }
}

Examples

cURL
curl --location 'https://landportal.com/wp-json/lp-rest-api/v1/skip-trace' \
--header 'Authorization: Bearer <JWT_TOKEN>' \
--header 'Content-Type: application/json' \
--data '{"method":"stop","task_id":12345}'
Python (requests)
import requests

url = "https://landportal.com/wp-json/lp-rest-api/v1/skip-trace"
headers = {
    "Authorization": "Bearer <JWT_TOKEN>",
    "Content-Type": "application/json"
}
response = requests.post(url, json={"method": "stop", "task_id": 12345}, headers=headers)
print(response.json())
JavaScript (fetch)
const response = await fetch("https://landportal.com/wp-json/lp-rest-api/v1/skip-trace", {
  method: "POST",
  headers: {
    "Authorization": "Bearer <JWT_TOKEN>",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({ method: "stop", task_id: 12345 })
});
console.log(await response.json());

4. Continue Task

Endpoint: POST /skip-trace
Required Parameters:
  • method — Must be "continue"
  • task_id — The ID of the task to continue
Note: Resumes a previously stopped task. The task will continue from where it was stopped and will be assigned a new Celery task ID.

Request Body

{
  "method": "continue",
  "task_id": 12345
}

Success Response

{
  "success": true,
  "message": "Skip trace task continued successfully",
  "data": { "response_code": 200 }
}

Examples

cURL
curl --location 'https://landportal.com/wp-json/lp-rest-api/v1/skip-trace' \
--header 'Authorization: Bearer <JWT_TOKEN>' \
--header 'Content-Type: application/json' \
--data '{"method":"continue","task_id":12345}'
Python (requests)
import requests

url = "https://landportal.com/wp-json/lp-rest-api/v1/skip-trace"
headers = {
    "Authorization": "Bearer <JWT_TOKEN>",
    "Content-Type": "application/json"
}
response = requests.post(url, json={"method": "continue", "task_id": 12345}, headers=headers)
print(response.json())
JavaScript (fetch)
const response = await fetch("https://landportal.com/wp-json/lp-rest-api/v1/skip-trace", {
  method: "POST",
  headers: {
    "Authorization": "Bearer <JWT_TOKEN>",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({ method: "continue", task_id: 12345 })
});
console.log(await response.json());

5. Create Single Skip Trace

Performs an instant, single-record skip trace: looks up contact data via Versium and scrubs returned phone numbers through TCP Litigator. No file or CSV is generated — the raw API responses are returned directly.

Endpoint: POST /skip-trace
Headers:
Content-Type: application/json
Authorization: Bearer <JWT_TOKEN>
Required Conditions:
  • Active subscription
  • single_skip_trace_limit > 0 (decremented on each successful call; resets daily at midnight Eastern Time)

Request Body

{
  "method": "create_single",
  "first": "John",
  "last": "Smith",
  "address": "123 Main St",
  "city": "Austin",
  "state": "TX",
  "zip": "78701"
}

This API service accepts a consumer first and last name, and any other known consumer contact information. The more fields provided, the more accurate the match.

FieldTypeRequiredDescription
firststringNoFirst name
laststringNoLast name
addressstringNoA house/building number and street. Any address associated with the consumer improves match accuracy.
citystringNoCity
statestringNoState (2-letter code, e.g. TX)
zipstringNoZIP code

Success Response

{
  "success": true,
  "meta": { "daily_requests_left": 49 },
  "data": {
    "task_id": 12345,
    "timestamp": "2026-04-10 14:00:00",
    "first": "John",
    "last": "Smith",
    "address": "123 Main St",
    "city": "Austin",
    "state": "TX",
    "zip": "78701",
    "phones": [
      { "phone": "5125550100", "line_type": "Mobile",   "dnc": false, "dnc_types": [] },
      { "phone": "5125550101", "line_type": "Landline", "dnc": true,  "dnc_types": ["tcpa"] }
    ]
  }
}

Error Response — Limit Exhausted

{
  "success": false,
  "message": "Single skip trace limit exhausted"
}

HTTP 403. The daily limit resets automatically at midnight Eastern Time.

Example (cURL)

curl --location 'https://landportal.com/wp-json/lp-rest-api/v1/skip-trace' \
--header 'Authorization: Bearer <JWT_TOKEN>' \
--header 'Content-Type: application/json' \
--data '{
  "method": "create_single",
  "first": "John",
  "last": "Smith",
  "address": "123 Main St",
  "city": "Austin",
  "state": "TX",
  "zip": "78701"
}'

Errors

401 Unauthorized

{ "success": false, "message": "User not authenticated" }

400 Validation

{ "success": false, "message": "Input file or JSON data is required" }

403 Forbidden

{ "success": false, "message": "You are not authorized to get this skip trace" }

500 Server Error

{ "success": false, "message": "Internal server error occurred" }

File Handling

  • Supported format: CSV
  • Encoding: UTF-8
  • Max size: Limited by available tokens
  • URLs supported: Direct links, Google Drive links

Token System

  • 1 row = 1 token
  • Tokens are deducted on task creation
  • Failed rows refund tokens

Comp Reports API

The Comp Reports API allows you to generate and retrieve comp reports for a given property. Report generation is asynchronous — use the GET endpoint to fetch the result once ready.

Base URL:
https://landportal.com/wp-json/lp-rest-api/v1

Authentication

All requests must include a JWT token:

Authorization: Bearer <JWT_TOKEN>

POST /reports — Create Comp Report

Endpoint: POST /reports
Headers:
Content-Type: application/json
Authorization: Bearer <JWT_TOKEN>
Required Conditions:
  • Active API access
  • comp_reports_limit > 0 (decremented on each successful call; resets daily at midnight Eastern Time)

Request Body

FieldTypeRequiredDescription
propertyidstringYesProperty ID from LandPortal database.
fipsstringYes5-digit FIPS code identifying the county.
{
  "propertyid": "12345678",
  "fips": "12345"
}

Success Response

{
  "success": true,
  "meta": { "daily_requests_left": 49 },
  "data": {
    "task_id": 4821,
    "property_id": "12345678",
    "fips": "12345",
    "timestamp": "2026-04-10 00:00:00"
  }
}

Examples

cURL
curl -X POST 'https://landportal.com/wp-json/lp-rest-api/v1/reports' \
  -H 'Authorization: Bearer <JWT_TOKEN>' \
  -H 'Content-Type: application/json' \
  -d '{"propertyid":"12345678","fips":"12345"}'
Python (requests)
import requests

url = "https://landportal.com/wp-json/lp-rest-api/v1/reports"
headers = {
    "Authorization": "Bearer <JWT_TOKEN>",
    "Content-Type": "application/json"
}
response = requests.post(url, json={"propertyid": "12345678", "fips": "12345"}, headers=headers)
print(response.json())
JavaScript (fetch)
const response = await fetch("https://landportal.com/wp-json/lp-rest-api/v1/reports", {
  method: "POST",
  headers: {
    "Authorization": "Bearer <JWT_TOKEN>",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({ propertyid: "12345678", fips: "12345" })
});
console.log(await response.json());

GET /reports — Get Comp Report

Endpoint: GET /reports
Headers:
Authorization: Bearer <JWT_TOKEN>

Query Parameters

ParameterTypeRequiredDescription
propertyidstringYesProperty ID from LandPortal database.
fipsstringYes5-digit FIPS code identifying the county.

Success Response

{
  "success": true,
  "data": {
    "location": "County, State",
    "address": "Property Street Address",
    "apn": "Assessor Parcel Number",
    "link": "https://landportal.com/?property=encoded_property_link",
    "zipcode": 12345,
    "usecode": 1006,
    "landuse": "Property Land Use Description",
    "size": "Property size in acres",
    "land_locked": false,
    "road_frontage": "Road frontage in feet",
    "wetlands_cover_percentage": "Percentage of wetlands coverage",
    "fema_cover_percentage": "Percentage of FEMA flood zone coverage",
    "total_our_estimation_values_base": "Total estimated property value",
    "price_acre_our_estimation_values_base": "Price per acre (our estimation)",
    "price_acre_mean": "Mean price per acre",
    "price_acre_county": "County average price per acre",
    "report_id": 123,
    "updated_at": "2026-04-10 10:30:00.000000"
  }
}

Examples

cURL
curl -X GET 'https://landportal.com/wp-json/lp-rest-api/v1/reports?propertyid=12345678&fips=12345' \
  -H 'Authorization: Bearer <JWT_TOKEN>'
Python (requests)
import requests

url = "https://landportal.com/wp-json/lp-rest-api/v1/reports"
headers = {"Authorization": "Bearer <JWT_TOKEN>"}
params = {"propertyid": "12345678", "fips": "12345"}

response = requests.get(url, params=params, headers=headers)
print(response.json())
JavaScript (fetch)
const response = await fetch(
  "https://landportal.com/wp-json/lp-rest-api/v1/reports?propertyid=12345678&fips=12345",
  { headers: { "Authorization": "Bearer <JWT_TOKEN>" } }
);
console.log(await response.json());

Errors

StatusMessageCause
400propertyid and fips are requiredMissing required parameters.
400Wrong fipsNo county database found for the given FIPS.
400Report not foundNo report exists for this property.
403Comp report limit exhaustedDaily quota exhausted.
403API access disabledAPI access is disabled for this user.
500Database error occurredInternal server error.

Export API

The Export API creates asynchronous CSV export tasks, tracks their status, and returns downloadable file links when processing is complete.

Base URL:
https://landportal.com/wp-json/lp-rest-api/v1

Authentication

All requests must include a JWT token:

Authorization: Bearer <JWT_TOKEN>
Required Conditions:
  • Active API access
  • Active subscription export tokens balance
  • For POST /export: valid fips and propertyid arrays

POST /export — Create Export Task

Endpoint: POST /export
Headers:
Content-Type: application/json
Authorization: Bearer <JWT_TOKEN>

Request Body

FieldTypeRequiredDescription
fipsarray<string|number>YesCounty FIPS list used to resolve source county tables. Each item must be a 5-digit FIPS code.
propertyidarray<string|number>YesProperty IDs to export. Each item must be a positive bigint.

Normalization: fips and propertyid values can be sent as strings or numbers. API normalizes them before task persistence.

Billing: 1 propertyid = 1 export token. Debit happens on successful task creation.

{
  "fips": ["12103", "06037"],
  "propertyid": [123456789, "987654321"]
}

Success Response

{
  "success": true,
  "data": {
    "task_id": 65432,
    "export_tokens_left": 998,
    "message": "Export task queued"
  }
}

Personal cabinet behavior: Export task history in your account shows task metadata (status, counters, timestamps, links). Full input arrays are stored in the payload file; the cabinet provides a link/path to that payload file with complete fips/propertyid data.

Examples

cURL
curl -X POST "https://landportal.com/wp-json/lp-rest-api/v1/export" \
  -H "Authorization: Bearer <JWT_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "fips": ["12103", "06037"],
    "propertyid": [123456789, "987654321"]
  }'
Python (requests)
import requests

url = "https://landportal.com/wp-json/lp-rest-api/v1/export"
headers = {
    "Authorization": "Bearer <JWT_TOKEN>",
    "Content-Type": "application/json"
}
payload = {
    "fips": ["12103", "06037"],
    "propertyid": [123456789, "987654321"]
}

response = requests.post(url, json=payload, headers=headers)
print(response.json())
JavaScript (fetch)
const response = await fetch("https://landportal.com/wp-json/lp-rest-api/v1/export", {
  method: "POST",
  headers: {
    "Authorization": "Bearer <JWT_TOKEN>",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    fips: ["12103", "06037"],
    propertyid: [123456789, "987654321"]
  })
});
console.log(await response.json());

GET /export — Get Task Status

Endpoint: GET /export
Headers:
Authorization: Bearer <JWT_TOKEN>

Query Parameters

ParameterTypeRequiredDescription
task_idintegerYesExport task ID returned by POST /export.

Task Status Values

StatusDescription
queuedTask is queued and waiting for worker pickup.
processingWorker is collecting data and building CSV output.
completedCSV is generated and uploaded; file_url is available.
failedTask failed. Check error_message for details.

Success Responses

{
  "success": true,
  "data": {
    "task_id": 65432,
    "status": "processing"
  }
}
{
  "success": true,
  "data": {
    "task_id": 65432,
    "status": "completed",
    "file_url": "https://landportal-exports.s3.us-east-2.amazonaws.com/exports/api_export_65432.csv",
    "rows": 2
  }
}

Example

curl -X GET "https://landportal.com/wp-json/lp-rest-api/v1/export?task_id=65432" \
  -H "Authorization: Bearer <JWT_TOKEN>"

GET /export/tasks — List My Export Tasks

Endpoint: GET /export/tasks
Headers:
Authorization: Bearer <JWT_TOKEN>

Query Parameters

ParameterTypeRequiredDescription
pageintegerNoPage number. Default: 1.
per_pageintegerNoPage size. Default: 50, max: 200.

Success Response

{
  "success": true,
  "data": {
    "tasks": [
      {
        "task_id": 65433,
        "status": "completed",
        "created_at": "2026-05-23 13:58:01",
        "updated_at": "2026-05-23 13:58:45",
        "rows": 2,
        "file_url": "https://landportal-exports.s3.us-east-2.amazonaws.com/exports/api_export_65433.csv",
        "error_message": false
      },
      {
        "task_id": 65432,
        "status": "processing",
        "created_at": "2026-05-23 13:57:10",
        "updated_at": "2026-05-23 13:57:15",
        "rows": 0,
        "file_url": "",
        "error_message": false
      }
    ],
    "pagination": {
      "page": 1,
      "per_page": 50,
      "total": 17
    }
  }
}

pagination.total — total number of tasks for your account (not total pages). See the Technical Recommendations tab for pagination details.

Example

curl -X GET "https://landportal.com/wp-json/lp-rest-api/v1/export/tasks?page=1&per_page=50" \
  -H "Authorization: Bearer <JWT_TOKEN>"

Errors

StatusMessageCause
400Invalid request bodyMalformed JSON body.
400fips must be an arrayMissing or invalid fips.
400propertyid must be an arrayMissing or invalid propertyid.
400task_id is requiredMissing or invalid task_id in GET /export.
403Insufficient export tokens limitNot enough export tokens to create task. Error payload includes required_export_tokens and available_export_tokens.
403You are not authorized to access this taskTask belongs to another user.
404Task not foundUnknown task ID.
500Failed to queue export taskTask persistence/scheduler failure.

Property Data API

Returns detailed data for a single property record by Property ID and FIPS code. The set of returned fields is determined by the per-user and global field configuration in admin settings.

Base URL:
https://landportal.com/wp-json/lp-rest-api/v1

Authentication

All requests must include a JWT token:

Authorization: Bearer <JWT_TOKEN>

Get Property Data

Endpoint: GET /property-data
Headers:
Authorization: Bearer <JWT_TOKEN>
Required Conditions:
  • Active API access
  • Available single-property balance (daily single_property_limit is used first, then subscription export tokens when available)

Query Parameters

ParameterTypeRequiredDescription
propertyid + fipsstring + stringOne of twoPrimary lookup pair (Property ID + 5-digit FIPS).
lat + lngnumber + numberOne of twoAlternative lookup pair by point.
Lookup rules:
  • If propertyid + fips are provided, they take precedence.
  • If not, the request uses lat + lng to resolve a property.

Quota consumption order: the API consumes single_property_limit first; once it reaches 0, it consumes subscription export tokens (if available).

Meta counter: meta.requests_left is the combined remaining balance for this endpoint (daily single-property quota + available export tokens).

Success Response

{
  "success": true,
  "meta": {
    "requests_left": 49
  },
  "data": {
    "property": {
      "propertyid": "12345678",
      "fips": "12345",
      "address": "123 Main St",
      "city": "Austin",
      "state": "TX",
      "zip": "78701"
    }
  }
}

Examples

cURL
curl -X GET "https://landportal.com/wp-json/lp-rest-api/v1/property-data?propertyid=12345678&fips=12345" \
  -H "Authorization: Bearer <JWT_TOKEN>"
cURL (by point)
curl -X GET "https://landportal.com/wp-json/lp-rest-api/v1/property-data?lat=30.2672&lng=-97.7431" \
  -H "Authorization: Bearer <JWT_TOKEN>"
Python (requests)
import requests

url = "https://landportal.com/wp-json/lp-rest-api/v1/property-data"
headers = {"Authorization": "Bearer <JWT_TOKEN>"}
params = {"propertyid": "12345678", "fips": "12345"}

response = requests.get(url, params=params, headers=headers)
print(response.json())
Python (requests, by point)
import requests

url = "https://landportal.com/wp-json/lp-rest-api/v1/property-data"
headers = {"Authorization": "Bearer <JWT_TOKEN>"}
params = {"lat": 30.2672, "lng": -97.7431}

response = requests.get(url, params=params, headers=headers)
print(response.json())
JavaScript (fetch)
const response = await fetch(
  "https://landportal.com/wp-json/lp-rest-api/v1/property-data?propertyid=12345678&fips=12345",
  { headers: { "Authorization": "Bearer <JWT_TOKEN>" } }
);
console.log(await response.json());
JavaScript (fetch, by point)
const response = await fetch(
  "https://landportal.com/wp-json/lp-rest-api/v1/property-data?lat=30.2672&lng=-97.7431",
  { headers: { "Authorization": "Bearer <JWT_TOKEN>" } }
);
console.log(await response.json());

Errors

StatusMessageCause
400Either propertyid+fips or lat+lng is requiredMissing required query parameters.
400County not foundNo database found for the given FIPS code.
403Single property limit reachedBoth daily single-property quota and export tokens are exhausted (or export tokens are unavailable).
403API access disabledAPI access is disabled for this user.
404Property not foundNo record for the given propertyid + fips.
500Database error occurredInternal server error.

Filter Data API

Apply advanced filters and return matching properties.

Base URL:
https://landportal.com/wp-json/lp-rest-api/v1

Endpoints

  • GET /filter-data/filters-list — available filters and examples
  • GET /filter-data/filter-values — values dictionary for a specific filter
  • POST /filter-data/filter — apply filters and return matching properties

Source of truth: use GET /filter-data/filters-list to retrieve the latest supported filters, operators, and dynamic request examples.

Scope rule: filters.fips is required only when neither filters.polygon nor filters.bbox is provided.

FIPS condition: for filters.fips, comparison is optional and defaults to is.

FIPS value format: value can be either a single 5-digit FIPS string or an array of 5-digit FIPS strings.

Meta: skipped restricted filters are returned in meta.rejected_filters with key, value, and reason.

Usage Limits

Filter endpoint counter: POST /filter-data/filter returns meta.requests_left.

Single Property counter: GET /property-data also returns meta.requests_left.

filter_data_limit: decremented by 1 only for successful responses with non-zero result set. Not decremented for overflow (count > 50000) or error responses.

Supported Operators

Supported operator groups include boolean, condition, range, date, equals, and geometry filters (polygon, bbox).

Date Filters

Supported date formats: YYYY-MM-DD (ISO 8601, recommended) and legacy YYYYMMDD.

Supported comparisons: is_between, is_before, is_after, is.

Slope Filters

Supported keys: percentage_of_land_with_flat_slope_0_05, sum_up_to_5, sum_up_to_10, sum_up_to_15.

Min-only usage: slope filters support using only value.min (without value.max).

Owner Name Filters

Supported keys: currentsaleseller1fullname, ownername1full, ownername2full.

Matching behavior: partial and case-insensitive (owner-search style). You can pass part of a name (for example, "smith").

Comparisons: is and is_not.

Example (Owner Name, partial)

{
  "filters": {
    "fips": {
      "operator": "condition",
      "comparison": "is",
      "value": "39001"
    },
    "ownername1full": {
      "operator": "condition",
      "comparison": "is",
      "value": "smith"
    }
  }
}

Example (Exclude by partial)

{
  "filters": {
    "fips": {
      "operator": "condition",
      "comparison": "is",
      "value": "39001"
    },
    "currentsaleseller1fullname": {
      "operator": "condition",
      "comparison": "is_not",
      "value": "holdings"
    }
  }
}

nulls Flag

Accepted input forms: boolean (true/false), string ("true"/"false", "yes"/"no"), numeric (1/0), and numeric strings ("1"/"0").

Behavior: nulls: true includes records with missing/null date value; nulls: false returns only records that match the date condition.

Example (Last Sale Date)

{
  "filters": {
    "fips": {
      "operator": "condition",
      "comparison": "is",
      "value": "39001"
    },
    "currentsalerecordingdate": {
      "operator": "date",
      "comparison": "is_between",
      "value": {
        "min": "2025-12-01",
        "max": "2026-05-18"
      },
      "nulls": true
    }
  }
}

Example (Multiple FIPS)

{
  "filters": {
    "fips": {
      "operator": "condition",
      "comparison": "is",
      "value": ["39001", "39049", "39113"]
    },
    "vacant": {
      "operator": "boolean",
      "value": true
    }
  }
}

Geometry Filters

KeyValue formatDescription
filters.polygon string or string[] One or more polygons. Each ring is a comma-separated list of "lon lat" pairs (min 4 pairs). Single string or array of strings; semicolon-separated string also accepted.
filters.bbox string Bounding box as exactly 2 "lon lat" pairs separated by a comma: top-left and bottom-right.

Scope: filters.fips is required only when neither filters.polygon nor filters.bbox is provided.

Multipolygon — semicolon-separated string: "ring1_coords;ring2_coords"

Multipolygon — array of strings: ["ring1_coords", "ring2_coords"]

Both formats produce the same Elasticsearch geo_shape multipolygon query. A single ring produces a polygon query.

Example — single polygon

{
  "filters": {
    "polygon": {
      "value": "-83.9135 33.6435,-83.9169 33.6256,-83.9057 33.6193,-83.8946 33.6164,-83.9135 33.6435"
    },
    "vacant": { "operator": "boolean", "value": true }
  }
}

Example — multipolygon as array of strings

{
  "filters": {
    "polygon": {
      "value": [
        "-83.9135 33.6435,-83.9169 33.6256,-83.9057 33.6193,-83.8946 33.6164,-83.9135 33.6435",
        "-84.1020 33.7510,-84.1150 33.7310,-84.0940 33.7200,-84.0800 33.7350,-84.1020 33.7510"
      ]
    }
  }
}

Example — multipolygon as semicolon-separated string

{
  "filters": {
    "polygon": {
      "value": "-83.9135 33.6435,-83.9169 33.6256,-83.9057 33.6193,-83.8946 33.6164,-83.9135 33.6435;-84.1020 33.7510,-84.1150 33.7310,-84.0940 33.7200,-84.0800 33.7350,-84.1020 33.7510"
    }
  }
}

Deduplication Parameters

These are top-level request body parameters, placed alongside filters (not inside it).

ParameterTypeDefaultDescription
duplicatesbooleantrue When false, deduplicates results by mailingfullstreetaddress, keeping the record with the largest lotsizeacres per unique address.
empty_mailing_addressesbooleantrue When false, excludes properties where mailingfullstreetaddress is empty or missing.

Accepted input forms: native boolean (true/false), string ("true"/"false"), or numeric (1/0).

Empty addresses with duplicates: false: when empty_mailing_addresses is true (default), properties with an empty or missing mailing address are not collapsed — each is returned as a unique entry.

count field: when duplicates: false, count reflects the actual post-deduplication number of returned properties, not the raw index total.

duplicatesempty_mailing_addressesBehaviour
true (default)true (default)All matching results, no changes.
truefalseEmpty/missing mailing address excluded; no deduplication.
falsetrue (default)Deduplicated by mailing address (largest lotsizeacres kept); empty-mailing properties each returned as a unique entry.
falsefalseDeduplicated by mailing address; empty/missing mailing address excluded entirely.

Example — deduplicate, keep empty-mailing as unique entries

{
  "duplicates": false,
  "filters": {
    "fips": {
      "operator": "condition",
      "value": "39001"
    },
    "lotsizeacres": {
      "operator": "range",
      "value": { "min": 5, "max": 50 }
    }
  }
}

Example — deduplicate and exclude empty mailing addresses

{
  "duplicates": false,
  "empty_mailing_addresses": false,
  "filters": {
    "fips": {
      "operator": "condition",
      "value": "39001"
    },
    "vacant": {
      "operator": "boolean",
      "value": true
    }
  }
}

Limit Overflow Response

{
  "success": true,
  "meta": {
    "requests_left": 145,
    "message": "Result set is limited to 50000 records. Please narrow your filters."
  },
  "data": {
    "count": 73412,
    "properties": []
  }
}

When result set exceeds 50000, API returns an empty properties array and recommends narrowing filters. In this case, filter_data_limit is not decremented.

Rejected Filters Example

{
  "success": true,
  "meta": {
    "requests_left": 49,
    "rejected_filters": [
      {
        "key": "unknown_filter",
        "value": {
          "operator": "range",
          "value": {
            "min": 5,
            "max": 10
          }
        },
        "reason": "This filter is invalid. Use GET /filter-data/filters-list to retrieve the latest supported filters and examples."
      }
    ]
  },
  "data": {
    "count": 451,
    "properties": [
      {
        "fips": "39001",
        "apn": "048-13-03-018.000",
        "situsfullstreetaddress": "19257 STATE ROUTE 136"
      }
    ]
  }
}

Technical Recommendations

General integration guidelines that apply across all API endpoints.

Async Polling

Applies to: POST /reports, POST /export, bulk POST /skip-trace (create).

Recommended interval: poll every 3–5 seconds. Stop as soon as status is completed or failed. Polling more frequently does not speed up processing and adds unnecessary load.

Terminal statuses: completed, failed, stopped — no further polling needed once reached.

Pagination

Endpoints that return lists (e.g. GET /export/tasks) include a pagination object:

"pagination": {
  "page": 1,
  "per_page": 50,
  "total": 17
}

total — total number of items (not pages). Calculate total pages as Math.ceil(total / per_page).

If page > Math.ceil(total / per_page), the response returns an empty tasks array.

Export Billing and Partial Match

Tokens are charged on task creation, before background processing starts. The charge equals the number of unique propertyid values after de-duplication.

Duplicate inputs: duplicate values in fips and propertyid are silently de-duplicated before billing and query execution.

Partial match: if some propertyid values do not exist in the county tables resolved from the given fips, only matching rows appear in the CSV. The task completes successfully with fewer rows than requested. Tokens are not refunded for unmatched IDs.

No match at all: if none of the propertyid values are found, the task transitions to failed with error_message: "The query returned no results". Tokens are not refunded.

Rate Limits

Dedicated HTTP rate-limit headers (e.g. X-RateLimit-*) are not returned. Monitor your remaining quota via response fields:

  • meta.requests_left — filter data, property data, search
  • meta.daily_requests_left — comp reports, skip-trace create_single
  • data.export_tokens_left — export token balance after task creation

Retries

Retry: transient failures only — network errors, timeouts, 5xx responses.

Do not retry: 4xx errors (validation failures, quota exhausted, auth errors) — they will not resolve on retry.

Strategy: exponential backoff with jitter — e.g. 1s → 2s → 4s, capped at 30s.

Timeout Expectations

Most filter, search, and property-data requests complete in well under a second. Exceptions:

  • Large-area or complex polygon filter queries can take several seconds.
  • Export and comp report tasks are asynchronous — use polling to check completion rather than waiting on the initial request.

Changelog

v1.3.5 — May 2026

  • filters.polygon.value now accepts an array of ring strings (["ring1", "ring2"]) in addition to the existing single-string and semicolon-separated formats.
  • All three polygon input formats (single string, ;-separated string, array) produce the same Elasticsearch geo_shape query — single ring becomes polygon, multiple rings become multipolygon.
  • Input normalisation and per-ring validation moved to FilterGeometryValidator: new parse_rings() method handles format detection; validate_polygon() validates each ring individually.

v1.3.4 — May 2026

  • Added top-level duplicates parameter to POST /filter-data/filter. When false, results are deduplicated by mailingfullstreetaddress, keeping the record with the largest lotsizeacres per unique address.
  • Added top-level empty_mailing_addresses parameter. When false, properties with an empty or missing mailingfullstreetaddress are excluded from results.
  • When duplicates: false and empty_mailing_addresses: true (default), properties without a mailing address are each returned as a unique entry rather than collapsed.
  • When duplicates: false, count in the response reflects the post-deduplication result count.
  • Both parameters accept boolean, string ("true"/"false"), and numeric (1/0) forms.

v1.3.3 — May 2026

  • Added Export API endpoints: POST /export, GET /export, and GET /export/tasks.
  • Export requests now support mixed numeric/string payload input for fips and propertyid, with server-side normalization.
  • Export token balance is validated before task creation; successful create responses include remaining balance in export_tokens_left.
  • GET /export/tasks always returns error_message for each task: string when failed, otherwise false.
  • Documented export task lifecycle and status polling flow (queued, processing, completed, failed).

v1.3.2 — May 2026

  • Filtered API date filters now accept ISO 8601 format YYYY-MM-DD (legacy YYYYMMDD remains supported).
  • Updated date examples returned by GET /filter-data/filters-list to use YYYY-MM-DD.
  • Date filters support nulls in multiple input forms: boolean (true/false), string ("true"/"false", "yes"/"no"), and numeric (1/0, including numeric strings).
  • nulls: true includes records with missing/null date values; nulls: false returns only records matching the date condition.
  • Slope keys aligned with web filters: percentage_of_land_with_flat_slope_0_05, sum_up_to_5, sum_up_to_10, sum_up_to_15.
  • Slope filter examples now explicitly include min-only usage (value.min without value.max).
  • Owner name filters now use owner-search-like matching (partial, case-insensitive) for seller/buyer and owner full-name fields.
  • Filter API usage limits clarified: responses return remaining quota in meta.requests_left.
  • Overflow behavior documented for filter responses where count > 50000.

v1.3.1 — April 2026

  • GET /property-data supports lookup by lat + lng when propertyid + fips are not provided.
  • GET /property-data returns combined remaining quota in meta.requests_left (daily single-property limit + export tokens, when available).
  • GET /property-data now uses export tokens after daily quota is exhausted (when available).
  • create_single: all fields are now optional. The address field accepts any address associated with the consumer.
  • create_single: response now includes line_type and dnc_types array per phone number.

v1.3.0 — April 2026

  • Added GET /reports endpoint for fetching existing comp reports.
  • Added search_limit daily quota for GET /search.
  • Added single_property_limit enforcement in GET /property-data.
  • Successful responses include remaining quota metadata where applicable (requests_left or daily_requests_left).
  • Service URLs (Comp Report, Versium, TCPA) moved to admin settings — no longer hardcoded.
  • Global daily limits moved from ACF to dedicated Usage Limits tab in API Settings.

v1.2.0 — March 2026

  • Introduced create_single skip-trace method with Versium + TCPA Litigator scrub.
  • Skip-trace finish now sends a webhook callback to the URL set in the user profile.
  • Webhook payload includes processing_time, average_hit_rate, credit_consumed, failed_rows.
  • Added Elasticsearch index settings to admin panel (Main Search Index field).

v1.1.0 — February 2026

  • Added GET /search endpoint.
  • Added per-user field selection for GET /property-data.
  • Introduced daily usage limits with midnight ET reset.
  • Added pause / continue skip-trace methods.

v1.0.0 — January 2026

  • Initial release.
  • GET /property-data, POST /reports, POST /skip-trace (create, get, stop, update, delete, finish).
  • JWT authentication.

Your Premium Source For Real Estate Data

© Copyright 2026 Land Portal – All Rights Reserved