Loading...
Everything you need to query the Hortus native plant database.
All API requests require an API key passed via the X-API-Key header.
curl -H "X-API-Key: hk_your_key_here" \ "https://joinhortus.com/api/v1/plants"
To get an API key, go to API Key Management to generate one, or use the key management API below.
https://joinhortus.com/api/v1
Query the native plant catalog. Returns approved plants matching your filters.
| Parameter | Type | Required | Description |
|---|---|---|---|
search | string | Optional | Free-text search across common and scientific names |
province | string | Optional | Filter by Canadian province code (AB, BC, MB, NB, NL, NS, NT, NU, ON, PE, QC, SK, YT) |
category | string | Optional | Plant category: wildflower, grass, fern, shrub, tree, vine, sedge, rush |
bloom_month | string | Optional | Filter by bloom month: january through december |
wildlife | string | Optional | Filter by wildlife attraction: bees, butterflies, hummingbirds, songbirds |
native_status | string | Optional | Filter by native status value |
limit | integer | Optional | Results per page (1-100, default: 20) |
offset | integer | Optional | Number of results to skip (default: 0) |
# Search for bee-attracting wildflowers in Ontario curl -H "X-API-Key: hk_your_key_here" \ "https://joinhortus.com/api/v1/plants?province=ON&category=wildflower&wildlife=bees&limit=10"
{
"data": [
{
"id": "uuid",
"commonName": "Wild Bergamot",
"scientificName": "Monarda fistulosa",
"category": "wildflower",
"nativeStatus": "native",
"bloomPeriod": "July-September",
"bloomMonths": ["July", "August", "September"],
"provinces": ["AB", "BC", "MB", "ON", "QC", "SK"],
"imageUrl": "https://..."
}
],
"meta": {
"total": 142,
"limit": 20,
"offset": 0,
"tier": "free",
"usage": {
"used": 42,
"limit": 500,
"resets_at": "2026-03-01T00:00:00.000Z"
}
}
}Included in all API responses.
| Field | Description |
|---|---|
id | Unique identifier (UUID) |
commonName | Common name |
scientificName | Scientific (Latin) name |
category | Plant type (wildflower, shrub, tree, etc.) |
nativeStatus | Native status classification |
bloomPeriod | Human-readable bloom time (e.g., "July-September") |
bloomMonths | Array of bloom months |
provinces | Array of province codes where native |
imageUrl | Plant image URL (may be null) |
Available on Starter, Pro, and Business plans.
| Field | Description |
|---|---|
attractsBees | Boolean — attracts bees |
attractsButterflies | Boolean — attracts butterflies |
attractsHummingbirds | Boolean — attracts hummingbirds |
attractsSongbirds | Boolean — attracts songbirds |
nectarSource | Boolean — is a nectar source |
larvalFoodSource | Boolean — is a larval food source |
deerResistant | Boolean — deer resistant |
lightRequirements | Array of light conditions |
soilMoisture | Preferred soil type |
height | Height description |
heightMinCm / heightMaxCm | Height range in centimeters |
flowerColors | Array of flower colors |
plantHabits | Array of growth habits |
hardinessZoneMin / Max | USDA hardiness zone range |
nativeHabitat | Natural habitat description |
familyName / genusName | Taxonomic classification |
ecoregions | Array of ecoregion codes |
vascanId | VASCAN database identifier |
Rate limits are enforced per API key on a monthly basis. The counter resets on the 1st of each month (UTC).
| Tier | Monthly Limit | Fields |
|---|---|---|
| Free | 500 requests | Basic fields |
| Starter | 5,000 requests | All fields |
| Pro | 25,000 requests | All fields + bulk |
| Business | Unlimited | Custom |
Rate limit info is included in response headers:
X-RateLimit-Limit: 500 X-RateLimit-Remaining: 458 X-RateLimit-Reset: 2026-03-01T00:00:00.000Z
curl -H "X-API-Key: hk_your_key_here" \ "https://joinhortus.com/api/v1/plants?province=ON&bloom_month=july&limit=5"
const response = await fetch(
"https://joinhortus.com/api/v1/plants?province=ON&wildlife=bees",
{
headers: { "X-API-Key": process.env.HORTUS_API_KEY }
}
);
const { data, meta } = await response.json();
console.log(`Found ${meta.total} plants, showing ${data.length}`);import requests
response = requests.get(
"https://joinhortus.com/api/v1/plants",
headers={"X-API-Key": "hk_your_key_here"},
params={"province": "ON", "category": "wildflower", "limit": 10}
)
data = response.json()
for plant in data["data"]:
print(f"{plant['commonName']} ({plant['scientificName']})")| Status | Meaning | Resolution |
|---|---|---|
| 400 | Bad Request | Check query parameter values |
| 401 | Unauthorized | Missing or invalid API key |
| 429 | Rate Limited | Monthly limit exceeded — wait for reset or upgrade |
| 500 | Server Error | Retry later or contact support |
{
"error": "Monthly rate limit exceeded"
}Manage API keys programmatically. These endpoints require session authentication (you must be logged in).
Create a new API key. Maximum 3 active keys per account.
curl -X POST "https://joinhortus.com/api/v1/keys" \
-H "Content-Type: application/json" \
-H "Cookie: <session_cookie>" \
-d '{"name": "My App"}'
# Response (201)
{
"key": "hk_a1b2c3d4e5f6...",
"prefix": "hk_a1b2c3d4",
"name": "My App",
"tier": "free",
"monthlyLimit": 500,
"message": "Store this key securely. It will not be shown again."
}List your API keys with usage statistics.
{
"keys": [
{
"id": "uuid",
"name": "My App",
"keyPrefix": "hk_a1b2c3d4",
"tier": "free",
"monthlyLimit": 500,
"requestCount": 42,
"isActive": true,
"lastUsedAt": "2026-02-17T10:30:00Z",
"createdAt": "2026-02-01T00:00:00Z"
}
]
}Revoke an API key. This action cannot be undone.
curl -X DELETE "https://joinhortus.com/api/v1/keys/<key-uuid>" \
-H "Cookie: <session_cookie>"
# Response (200)
{ "message": "API key revoked" }