Skip to content
Terraledger

Search records

Search wells, operators, leases and properties

Build on it

API reference

A versioned REST API over every record on the site — wells, permits, operators, production, Texas ownership — with provenance and freshness on every response.

Overview

Base URL: /api/v1 (same origin as this site) or https://terraledger.ayrical.com/api/v1 externally. The API is versioned by path segment (v1); breaking changes ship as v2 rather than mutating v1 in place.

Access & rate limits

Ordinary read requests work without credentials. Anonymous callers are limited by client address to 60 requests per minute. Send an optional x-api-key for a stable caller identity, a separately assigned limit, async export ownership, or a source-specific record family that requires an audit identity.

An invalid or revoked supplied key returns 401 unauthorized. A key that lacks a reviewed source-specific scope returns 403 forbidden_plan. Rate-limited API responses publish RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset. A 429 also carries Retry-After in seconds.

Requests accept at most 20 filter clauses, each no longer than 500 characters; q must be 3–200 characters and JSON request bodies are limited to 64 KiB.

Endpoints

Implemented v1 routes, grouped as they appear in the client. Detail routes take a numeric id except wells, which also accept the API/UWI.

Endpoint catalog

36 routes

MethodPathDescriptionKey paramsAccess
GET/openapi.jsonOpenAPI 3.1 discovery document for agents and generated clients.Anonymous
GET/healthWorker and database readiness, plus optional capability status.Anonymous
GET/coverageAggregate counts — wells, operators, states, periods covered.Anonymous
GET/sourcesRegistry of upstream agencies and systems the data is sourced from.filter, sort, cursor, limitAnonymous
GET/freshnessRetrieval and publish timestamps per dataset.Anonymous
GET/schemasMachine-readable field definitions for every record type.Anonymous
GET/searchCross-entity text search across wells, operators, leases and properties.q, limitAnonymous
GET/wellsWell search with quick search and the full filter grammar.q, filter, sort, cursor, limitAnonymous
GET/wells/{id}Well detail — header, nearby wells, permit and frac-disclosure counts.Anonymous
GET/operatorsOperator directory.q, filter, sort, cursor, limitAnonymous
GET/operators/{id}Operator profile with production summary.Anonymous
GET/leasesLease search.q, filter, sort, cursor, limitAnonymous
GET/leases/{id}Lease detail.Anonymous
GET/propertiesProperty search.q, filter, sort, cursor, limitAnonymous
GET/properties/{id}Property detail.Anonymous
GET/permitsDrilling permits.q, filter, sort, cursor, limitAnonymous
GET/productionProduction records at well/lease/property/operator/operator-county grain, with record_status.filter, sort, cursor, limitAnonymous
GET/well-testsPoint-in-time well test rates.filter, sort, cursor, limitAnonymous
GET/well-logsWell log index — type, depths, external file URL.filter, sort, cursor, limitAnonymous
GET/frac-disclosuresFracFocus hydraulic fracturing disclosures and chemicals.filter, sort, cursor, limitAnonymous
GET/ownersTexas mineral/royalty owners from county appraisal districts.q, filter, sort, cursor, limitAPI key
GET/mineral-interestsTexas mineral interests — decimal interest, appraisal value, legal description.q, filter, sort, cursor, limitAPI key
GET/purchasersTexas crude/gas purchasers.q, filter, sort, cursor, limitAnonymous
GET/salesTexas Comptroller severance-tax sales filings.q, filter, sort, cursor, limitAnonymous
GET/map/wellsIndividual well points inside a bounding box, for the map view.bbox, filter, limitAnonymous
GET/map/clustersDeterministic well clusters inside a bounding box at a zoom level.bbox, zoom, filterAnonymous
GET/tiles/wells/{z}/{x}/{y}.mvtMapbox Vector Tile of wells for the given tile coordinate.Anonymous
POST/geo/searchSearch wells around a point and radius.body: lat, lon, radius_km, limitAnonymous
GET/analytics/daily-productionNormalized daily production rates with reporting grain preserved.entity_type, idAnonymous
GET/analytics/boeCurrent barrel-of-oil-equivalent conversion inputs and formula.Anonymous
GET/forecasts/{entity_type}/{id}Decline-curve forecast for a well, lease, property or operator.Anonymous
POST/valuationsCompute a DCF or market-multiple valuation for an entity.body: entity_type, entity_id, ownership_decimal, oil_price_usd, gas_price_usd, discount_rateAnonymous
POST/exportsQueue an async export job for a dataset with the current filter/sort/params.body: dataset, format, paramsAPI key
GET/exports/{job_id}Poll an export job's status; download completed artifacts through its download route.API key
DELETE/exports/{job_id}Cancel a queued or running export job.API key
GET/exports/{job_id}/downloadDownload a completed export owned by the supplied API key.API key

Filter grammar

Every list endpoint accepts repeatable filter query params in the form filter=<field>:<op>:<value>. Multi-value operators (in, between) join their values with a pipe.

GET /v1/wells?filter=state_code:eq:TX&filter=cum_oil_bbl:gt:100000&filter=status:in:producing|shut_in

Operators

12 ops

OpMeaning
eqExact match.
neqExcludes exact match.
containsCase-insensitive substring match.
ncontainsExcludes case-insensitive substring match.
ltLess than.
lteLess than or equal to.
gtGreater than.
gteGreater than or equal to.
inMatches any of a list of values, pipe-joined.
betweenInclusive range — two pipe-joined values, low then high.
is_nullField is null or missing. No value.
not_nullField is present. No value.

sort=field sorts ascending, sort=-field descending. Pagination is cursor-based: cursor is an opaque token from the previous response’s meta.next_cursor; limit caps the page size at 500 (default 50).

Response envelope

List endpoints return a data array plus a meta block carrying an exact or explicitly capped total, the cursor for the next page, and freshness.

List envelope

{
  "data": [
    {
      "id": 48213,
      "api_number": "4212330001",
      "name": "SPRABERRY UNIT 12",
      "operator_name": "PIONEER NATURAL RESOURCES",
      "state_code": "TX",
      "county": "Midland",
      "status": "producing",
      "cum_oil_bbl": 184220
    }
  ],
  "meta": {
    "next_cursor": "eyJvIjo1MH0",
    "total": 4821,
    "total_capped": false,
    "returned": 50,
    "source_period": "2026-05",
    "retrieved_at": "2026-06-02T14:05:00Z",
    "published_at": "2026-06-03T09:00:00Z",
    "as_of": "2026-08-05T18:22:11Z"
  }
}

Detail envelope

{
  "data": {
    "id": 48213,
    "api_number": "4212330001",
    "name": "SPRABERRY UNIT 12",
    "status": "producing",
    "nearby_wells": []
  },
  "meta": {
    "source_period": "2026-05",
    "retrieved_at": "2026-06-02T14:05:00Z",
    "published_at": "2026-06-03T09:00:00Z",
    "as_of": "2026-08-05T18:22:11Z"
  }
}

Error envelope

{
  "error": {
    "code": "rate_limited",
    "message": "Rate limit exceeded"
  }
}

Error codes

7 codes

CodeMeaning
unauthorizedAn API key was required, invalid, or revoked.
forbidden_planThe supplied key lacks a source-specific data scope.
not_foundNo endpoint or record at this path.
invalid_requestMalformed filter, sort, or parameter.
rate_limitedToo many requests for this identity — see Retry-After.
internalThe API failed to answer. Retry shortly.
not_implementedSpecified, but unavailable in this deployment — vector tiles need PostGIS.

Record status

Every production figure carries a record_status so a filing is never read as a calculation. The four statuses are never mingled into one silent series — a chart or export that spans them keeps them as separate, labeled series.

reported
Filed as-is by the source agency or operator — not derived.
allocated
Split from a lease- or unit-level report down to a well, by a documented weight.
estimated
Modeled where no report exists, with method, inputs and confidence stored.
forecast
Projected beyond the reported history by a decline-curve model, versioned.

curl examples

Filtered well search, sorted, limited

curl "https://terraledger.ayrical.com/api/v1/wells?q=wolfcamp&filter=state_code:eq:TX&filter=cum_oil_bbl:gt:100000&sort=-cum_oil_bbl&limit=50"

Cursor follow-up (same filters, next page)

curl "https://terraledger.ayrical.com/api/v1/wells?q=wolfcamp&filter=state_code:eq:TX&filter=cum_oil_bbl:gt:100000&sort=-cum_oil_bbl&limit=50&cursor=eyJvIjo1MH0"

Map clusters for a bounding box at a zoom level

curl "https://terraledger.ayrical.com/api/v1/map/clusters?bbox=-103.5,31.0,-101.0,33.0&zoom=7"

Export lifecycle: queue, poll, download

# 1. Queue the export
curl -X POST -H "x-api-key: $TERRALEDGER_KEY" -H "Content-Type: application/json" \
  -d '{"dataset":"wells","format":"csv","params":{"filter":["state_code:eq:TX"]}}' \
  https://terraledger.ayrical.com/api/v1/exports

# 2. Poll until it leaves queued/running
curl -H "x-api-key: $TERRALEDGER_KEY" \
  https://terraledger.ayrical.com/api/v1/exports/exp_9f2c1a

# 3. Download once status is "completed"
curl -H "x-api-key: $TERRALEDGER_KEY" -OJ \
  https://terraledger.ayrical.com/api/v1/exports/exp_9f2c1a/download

Export lifecycle

An export is a job, not a download. POST /exports queues it against a dataset and the same filter/sort/params a list request would use; GET /exports/{job_id} polls it; DELETE /exports/{job_id} cancels a queued or running job.

Statuses
queuedrunningcompleted | failed | expired. A completed job carries row_count and is fetched from the documented download route; a failed job carries error.
Formats
csv, ndjson, geojson. Export jobs require an API key so status, cancellation and download remain scoped to the caller that created them.
Expiry
A completed job stays downloadable until its expires_at; after that its status becomes expired and the export must be re-queued.