BRD AgriAI API
A read-only REST API for the farm data already in your BRD AgriAI account — fields, weather, irrigation scheduling, soil health, yield history, equipment, and market prices. Script against your own farm.
Introduction
Every endpoint below is real and live — it's the same data and the same logic your BRD dashboard uses, exposed as JSON. This is v1.0, read-only: nothing in this API creates or modifies records in your account. Write access (logging a spray application, adding a harvest entry, etc.) may follow in a future version.
Prefer not to write HTTP calls by hand? See the official Python client library below, or explore every endpoint interactively in Swagger UI.
Authentication
Authenticate with a personal API key, generated at bishopresearch.com → Dashboard → Settings → API Keys. Pass it as a bearer token on every request:
Authorization: Bearer brd_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Base URL & versioning
https://bishopresearch.com/api/v1
Every path documented below (e.g. /fields) is relative to this base URL. The v1 in the path is the version — breaking changes will ship as a new version prefix rather than changing v1's behavior underneath you.
Rate limits
60 requests per minute, per API key. Exceeding it returns HTTP 429:
{"detail": "Rate limit exceeded: 60 requests/minute per API key."}The Python library retries 429 and 5xx responses automatically with exponential backoff before raising.
Errors
Errors are a JSON body with a single detail field describing what went wrong, and a standard HTTP status code:
| Status | Meaning |
|---|---|
401 | Missing, invalid, or revoked API key |
404 | Resource not found (or not yours) |
422 | Request understood but rejected — e.g. a field with no location set |
429 | Rate limit exceeded |
5xx | Something went wrong on BRD's end |
{"detail": "Field not found"}Account
GET/me
The account this API key belongs to.
curl https://bishopresearch.com/api/v1/me \ -H "Authorization: Bearer $BRD_API_KEY"
client.me()
Response — real example, captured live:
{
"email": "testfarmer999@example.com",
"full_name": "Test Farmer",
"subscription_tier": "seedling",
"created_at": "2026-03-15T21:20:47.707494Z"
}
Fields
GET/fields
List every field on your account.
curl https://bishopresearch.com/api/v1/fields \ -H "Authorization: Bearer $BRD_API_KEY"
client.fields.list()
Response — real example, captured live:
{
"fields": [
{
"id": 1,
"name": "North 80",
"acres": 80.0,
"crop": "Corn",
"soil_type": "Silt loam",
"lat": 41.5868,
"lon": -93.625,
"boundary": null,
"notes": "",
"agro_polygon_id": null,
"tillage_practice": "conventional",
"planting_date": null,
"created_at": "2026-03-15T21:23:06.463595Z",
"observation_count": 0
}
]
}
GET/fields/{field_id}
Get a single field by ID (must belong to your account, or this returns 404).
curl https://bishopresearch.com/api/v1/fields/1 \ -H "Authorization: Bearer $BRD_API_KEY"
client.fields.get(1)
Weather
GET/fields/{field_id}/weather
The current Open-Meteo forecast for a field's location. Returns 422 if the field has no lat/lon set.
curl https://bishopresearch.com/api/v1/fields/1/weather \ -H "Authorization: Bearer $BRD_API_KEY"
client.fields.weather(1)
Response — real example, captured live (forecast truncated to 1 of 7 days):
{
"field_id": 1,
"field_name": "North 80",
"forecast": [
{
"date": "2026-08-16",
"high": 78.3,
"low": 69.4,
"precip": 0.0,
"code": 3,
"et0": 0.11,
"wind_mph": 13.4
}
],
"cumulative_gdd": 2780.2,
"season_precip_in": 32.62,
"next7_total_precip": 0.79,
"frost_risk_7d": false,
"heavy_rain_7d": false,
"current_high": 78.3,
"current_low": 69.4
}
Irrigation
GET/fields/{field_id}/irrigation
FAO-56 irrigation schedule for a field, built from real ET0/precipitation forecasts and a crop Kc curve.
| Param | Type | Default | Notes |
|---|---|---|---|
days | int | 7 | Clamped to 1–14 |
curl "https://bishopresearch.com/api/v1/fields/1/irrigation?days=3" \ -H "Authorization: Bearer $BRD_API_KEY"
client.fields.irrigation(1, days=3)
Response — real example, captured live:
{
"field_id": 1,
"field_name": "North 80",
"crop": "Corn",
"kc_coefficient": 0.35,
"gdd_accumulated": 2780.0,
"gdd_since_planting": false,
"schedule": [
{
"date": "2026-08-16",
"et0_in": 0.11,
"etc_in": 0.038,
"precip_in": 0.0,
"daily_deficit_in": 0.038,
"cumulative_deficit_in": 0.038,
"irrigate": false
}
],
"total_et0_in": 0.38,
"total_etc_in": 0.13,
"total_precip_in": 0.55,
"days": 3
}
Soil Health
GET/soil-health
Cross-field soil health summary, built from your own recorded lab tests — latest test per field, a rating, deficit-based lime/P/K planning estimates, and farm-wide averages.
curl https://bishopresearch.com/api/v1/soil-health \ -H "Authorization: Bearer $BRD_API_KEY"
client.soil_health.summary()
Response — real example, captured live (no soil test recorded yet for this field):
{
"fields": [
{"field_id": 1, "field_name": "North 80", "acres": 80.0, "latest_test": null, "rating": "Unknown"}
],
"recommendations": [
{"field_id": 1, "field_name": "North 80", "lime_tons_ac": null, "p_lbs_ac": null, "k_lbs_ac": null, "cost_per_ac": null}
],
"aggregates": {"avg_ph": null, "avg_om": null, "avg_p": null, "avg_k": null},
"note": "Lime/P/K estimates are deficit-based planning figures from your latest soil test, not a calibrated lab buffer-pH recommendation. Confirm with your agronomist or lab before applying."
}
Yield History
GET/yield-history
Yield history built from your own recorded harvest entries — one point per field per season you've entered. No county/state averages are fabricated when you have no data yet.
curl https://bishopresearch.com/api/v1/yield-history \ -H "Authorization: Bearer $BRD_API_KEY"
client.yield_history.list()
Response — real example, captured live:
{
"fields": [
{"field_id": 1, "field_name": "North 80", "acres": 80.0, "crop": "Corn", "years": []}
],
"note": "Built from your entered harvest records (Harvest Log). Add entries there for fields with no history shown here."
}
The Python library's client.yield_history.list() returns the fields array directly.
Equipment
GET/equipment
Your logged equipment (a manually-maintained fleet inventory — name, year, make/model, hours, last service date, status).
curl https://bishopresearch.com/api/v1/equipment \ -H "Authorization: Bearer $BRD_API_KEY"
client.equipment.list()
Response — real example, captured live:
{"equipment": []}
Market Prices
GET/market/prices
Corn/soybean/wheat price estimates plus real day-over-day change (not field-specific). live: false means the server doesn't have a USDA AMS key configured and prices are estimates — check data_source.
curl https://bishopresearch.com/api/v1/market/prices \ -H "Authorization: Bearer $BRD_API_KEY"
client.market.prices()
Response — real example, captured live:
{
"corn": {"futures": 4.6, "change": 0.0, "change_pct": 0.0},
"soybeans": {"futures": 11.1, "change": 0.0, "change_pct": 0.0},
"wheat": {"futures": 6.1, "change": 0.0, "change_pct": 0.0},
"data_source": "Estimated (USDA_MARS_API_KEY not configured)",
"live": false,
"updated_at": "2026-08-16T21:22:59.701473"
}
Python Library
The official brdagriai package wraps every endpoint above in a resource-oriented client, with typed exceptions and automatic retry on rate limits / transient server errors.
Install
pip install brdagriai
Quickstart
# Generate a key at bishopresearch.com → Dashboard → Settings → API Keys from brdagriai import Client client = Client(api_key="brd_live_...") # or: export BRD_API_KEY=brd_live_... and just call Client() for field in client.fields.list(): schedule = client.fields.irrigation(field["id"], days=7) print(field["name"], schedule["schedule"])
Resource methods
| Call | Endpoint |
|---|---|
client.me() | GET /me |
client.fields.list() | GET /fields |
client.fields.get(id) | GET /fields/{id} |
client.fields.weather(id) | GET /fields/{id}/weather |
client.fields.irrigation(id, days=7) | GET /fields/{id}/irrigation |
client.soil_health.summary() | GET /soil-health |
client.yield_history.list() | GET /yield-history |
client.equipment.list() | GET /equipment |
client.market.prices() | GET /market/prices |
Errors
from brdagriai import Client, NotFoundError, RateLimitError, AuthenticationError
client = Client()
try:
client.fields.get(999999)
except NotFoundError:
print("no such field")
| Exception | HTTP status |
|---|---|
AuthenticationError | 401 |
NotFoundError | 404 |
ValidationError | 422 |
RateLimitError | 429 (after retries exhausted) |
ServerError | 5xx (after retries exhausted) |
ConnectionError | no response received (network/timeout) |
Context manager
with Client(api_key="brd_live_...") as client:
print(client.me())
# connection closed automatically
brdagriai package ships alongside this API — built on httpx, sync-only for now with an async client a natural (not yet built) follow-on. No pagination is implemented: every v1 resource returns a small, complete list scoped to your own farm.