Coral Tracker API

v1.0 — REST API Documentation

Overview

The Coral Tracker API lets you programmatically manage your coral inventory. Use it to integrate with e-commerce platforms, build custom dashboards, or sync data with mobile apps.

REST
JSON over HTTPS
10
Endpoints
Free
To get started

Base URL

https://api.coral-tracker.com/api/v1

Authentication

All requests must include your API key in the X-API-Key header. API keys start with ctk_.

Keep your key secret. Never expose it in client-side code, public repos, or URLs. If compromised, delete it and create a new one.

# Test your API key curl -H "X-API-Key: ctk_your_key_here" \ https://api.coral-tracker.com/api/v1/corals
const response = await fetch('https://api.coral-tracker.com/api/v1/corals', { headers: { 'X-API-Key': 'ctk_your_key_here', 'Accept': 'application/json' } }); const data = await response.json(); console.log(data);
<?php $ch = curl_init('https://api.coral-tracker.com/api/v1/corals'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'X-API-Key: ctk_your_key_here', 'Accept: application/json', ], ]); $response = curl_exec($ch); $data = json_decode($response, true); print_r($data);
import requests response = requests.get( 'https://api.coral-tracker.com/api/v1/corals', headers={'X-API-Key': 'ctk_your_key_here'} ) data = response.json() print(data)

Rate Limiting

Requests are rate-limited per organization. Check the response headers to monitor your usage:

Header Description
X-RateLimit-LimitMax requests per minute
X-RateLimit-RemainingRequests left in current window
X-RateLimit-ResetUnix timestamp when the window resets
Retry-AfterSeconds to wait (only on 429 responses)

Plans & Access

Free

$0

  • 1 API key (read-only)
  • 60 req/min
  • 10 corals
POPULAR

Premium

€4.99/mo

  • 5 API keys (read + write)
  • 120 req/min
  • Unlimited corals

Enterprise

€14.99/mo

  • Unlimited keys (full access)
  • 300 req/min
  • Webhooks
GET

/corals

Retrieve a paginated list of corals for your organization.

Query Parameters

ParamTypeDefaultDescription
per_pageinteger20Items per page (max 100)
pageinteger1Page number
for_salestringSet to true to only get corals marked for sale
typestringFilter by type: SPS, LPS, Soft, NPS, Anemone, Zoa, Other
searchstringSearch by name or species
curl -H "X-API-Key: ctk_your_key" \ "https://api.coral-tracker.com/api/v1/corals?for_sale=true&per_page=10"
const res = await fetch('https://api.coral-tracker.com/api/v1/corals?for_sale=true&per_page=10', { headers: { 'X-API-Key': 'ctk_your_key' } }); const { data, meta } = await res.json(); console.log(`Page ${meta.current_page} of ${meta.last_page}`);
$response = file_get_contents( 'https://api.coral-tracker.com/api/v1/corals?for_sale=true', false, stream_context_create(['http' => [ 'header' => "X-API-Key: ctk_your_key\r\nAccept: application/json" ]]) ); $data = json_decode($response, true);
response = requests.get( 'https://api.coral-tracker.com/api/v1/corals', headers={'X-API-Key': 'ctk_your_key'}, params={'for_sale': 'true', 'per_page': 10} ) corals = response.json()['data']

Example Response

{ "data": [ { "id": 1, "name": "Blue Acropora", "species": "Acropora millepora", "type": "SPS", "price_when_added": 49.99, "current_frag_count": 25, "health_status": "excellent", "is_for_sale": true, "image_url": "https://...", "gallery_urls": ["https://...", "https://..."], "images": [ { "id": 1, "image_url": "https://...", "is_primary": true } ] } ], "meta": { "current_page": 1, "per_page": 10, "total": 42, "last_page": 5 } }
GET

/corals/{id}

Retrieve a specific coral with its full update history and images.

curl -H "X-API-Key: ctk_your_key" \ https://api.coral-tracker.com/api/v1/corals/1
const res = await fetch('https://api.coral-tracker.com/api/v1/corals/1', { headers: { 'X-API-Key': 'ctk_your_key' } }); const coral = (await res.json()).data;
$ch = curl_init('https://api.coral-tracker.com/api/v1/corals/1'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ['X-API-Key: ctk_your_key'], ]); $coral = json_decode(curl_exec($ch), true)['data'];
POST

/corals

write permission

Create a new coral entry in your inventory.

Request Body

FieldTypeRequiredDescription
namestringYesCoral name (max 255)
speciesstringNoScientific species name
purchased_atdateYesPurchase date (YYYY-MM-DD)
price_when_addednumberYesPurchase price
price_per_fragnumberYesPrice per frag
initial_frag_countintegerYesStarting frag count (min: 1)
current_frag_countintegerNoDefaults to initial count
notesstringNoAny notes
health_statusstringNoexcellent, good, fair, or poor (default: good)
curl -X POST https://api.coral-tracker.com/api/v1/corals \ -H "X-API-Key: ctk_your_key" \ -H "Content-Type: application/json" \ -d '{ "name": "Blue Acropora", "species": "Acropora millepora", "purchased_at": "2026-01-15", "price_when_added": 49.99, "price_per_frag": 5.00, "initial_frag_count": 10, "health_status": "good" }'
const res = await fetch('https://api.coral-tracker.com/api/v1/corals', { method: 'POST', headers: { 'X-API-Key': 'ctk_your_key', 'Content-Type': 'application/json' }, body: JSON.stringify({ name: 'Blue Acropora', species: 'Acropora millepora', purchased_at: '2026-01-15', price_when_added: 49.99, price_per_frag: 5.00, initial_frag_count: 10, health_status: 'good' }) }); const newCoral = (await res.json()).data;
$ch = curl_init('https://api.coral-tracker.com/api/v1/corals'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ 'X-API-Key: ctk_your_key', 'Content-Type: application/json', ], CURLOPT_POSTFIELDS => json_encode([ 'name' => 'Blue Acropora', 'species' => 'Acropora millepora', 'purchased_at' => '2026-01-15', 'price_when_added' => 49.99, 'price_per_frag' => 5.00, 'initial_frag_count' => 10, ]), ]); $result = json_decode(curl_exec($ch), true);
PUT

/corals/{id}

write permission

Update an existing coral. Only include the fields you want to change.

curl -X PUT https://api.coral-tracker.com/api/v1/corals/1 \ -H "X-API-Key: ctk_your_key" \ -H "Content-Type: application/json" \ -d '{"current_frag_count": 25, "health_status": "excellent"}'
const res = await fetch('https://api.coral-tracker.com/api/v1/corals/1', { method: 'PUT', headers: { 'X-API-Key': 'ctk_your_key', 'Content-Type': 'application/json' }, body: JSON.stringify({ current_frag_count: 25, health_status: 'excellent' }) });
$ch = curl_init('https://api.coral-tracker.com/api/v1/corals/1'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => 'PUT', CURLOPT_HTTPHEADER => [ 'X-API-Key: ctk_your_key', 'Content-Type: application/json', ], CURLOPT_POSTFIELDS => json_encode([ 'current_frag_count' => 25, 'health_status' => 'excellent', ]), ]); $result = json_decode(curl_exec($ch), true);
DELETE

/corals/{id}

delete permission

Permanently delete a coral and all associated data.

Warning: This action is irreversible. The coral and all its update history will be permanently deleted.

curl -X DELETE https://api.coral-tracker.com/api/v1/corals/1 \ -H "X-API-Key: ctk_your_key"
await fetch('https://api.coral-tracker.com/api/v1/corals/1', { method: 'DELETE', headers: { 'X-API-Key': 'ctk_your_key' } });

Coral Updates

Track growth over time by recording snapshot updates for each coral. Each update captures frag count, price per frag, and an optional size measurement.

GET

/corals/{id}/updates

List all growth updates for a specific coral, newest first.

Query Parameters

ParamTypeDefaultDescription
per_pageinteger20Items per page (max 100)
pageinteger1Page number
curl -H "X-API-Key: ctk_your_key" \ "https://api.coral-tracker.com/api/v1/corals/1/updates"
const res = await fetch('https://api.coral-tracker.com/api/v1/corals/1/updates', { headers: { 'X-API-Key': 'ctk_your_key' } }); const { data, meta } = await res.json();
$response = file_get_contents( 'https://api.coral-tracker.com/api/v1/corals/1/updates', false, stream_context_create(['http' => [ 'header' => "X-API-Key: ctk_your_key\r\nAccept: application/json" ]]) ); $updates = json_decode($response, true)['data'];

Example Response

{ "data": [ { "id": 12, "coral_id": 1, "frag_count": 28, "price_per_frag": "6.50", "size_cm": "4.2", "total_value": 182.00, "notes": "Excellent polyp extension this week", "recorded_at": "2026-04-20" } ], "meta": { "current_page": 1, "per_page": 20, "total": 5, "last_page": 1 } }
GET

/corals/{id}/updates/{updateId}

Retrieve a single growth update by ID.

curl -H "X-API-Key: ctk_your_key" \ https://api.coral-tracker.com/api/v1/corals/1/updates/12
const res = await fetch('https://api.coral-tracker.com/api/v1/corals/1/updates/12', { headers: { 'X-API-Key': 'ctk_your_key' } }); const update = (await res.json()).data;
POST

/corals/{id}/updates

write permission

Record a new growth snapshot for a coral. This also syncs the coral's live current_frag_count and price_per_frag fields.

Request Body

FieldTypeRequiredDescription
frag_countintegerYesCurrent frag count (min: 0)
price_per_fragnumberYesCurrent market price per frag
size_cmnumberNoColony size in centimetres
notesstringNoObservations for this update
recorded_atdateNoDate of observation (default: today)
curl -X POST https://api.coral-tracker.com/api/v1/corals/1/updates \ -H "X-API-Key: ctk_your_key" \ -H "Content-Type: application/json" \ -d '{ "frag_count": 28, "price_per_frag": 6.50, "size_cm": 4.2, "notes": "Strong polyp extension after feeding", "recorded_at": "2026-04-26" }'
const res = await fetch('https://api.coral-tracker.com/api/v1/corals/1/updates', { method: 'POST', headers: { 'X-API-Key': 'ctk_your_key', 'Content-Type': 'application/json' }, body: JSON.stringify({ frag_count: 28, price_per_frag: 6.50, size_cm: 4.2, notes: 'Strong polyp extension after feeding' }) }); const update = (await res.json()).data;
$ch = curl_init('https://api.coral-tracker.com/api/v1/corals/1/updates'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ 'X-API-Key: ctk_your_key', 'Content-Type: application/json', ], CURLOPT_POSTFIELDS => json_encode([ 'frag_count' => 28, 'price_per_frag' => 6.50, 'size_cm' => 4.2, 'notes' => 'Strong polyp extension after feeding', ]), ]); $result = json_decode(curl_exec($ch), true);
PUT

/corals/{id}/updates/{updateId}

write permission

Edit an existing update. Only include the fields you want to change.

curl -X PUT https://api.coral-tracker.com/api/v1/corals/1/updates/12 \ -H "X-API-Key: ctk_your_key" \ -H "Content-Type: application/json" \ -d '{"notes": "Corrected observation note", "size_cm": 4.5}'
const res = await fetch('https://api.coral-tracker.com/api/v1/corals/1/updates/12', { method: 'PUT', headers: { 'X-API-Key': 'ctk_your_key', 'Content-Type': 'application/json' }, body: JSON.stringify({ notes: 'Corrected observation note', size_cm: 4.5 }) });
DELETE

/corals/{id}/updates/{updateId}

delete permission

Permanently remove a growth update record.

curl -X DELETE https://api.coral-tracker.com/api/v1/corals/1/updates/12 \ -H "X-API-Key: ctk_your_key"
await fetch('https://api.coral-tracker.com/api/v1/corals/1/updates/12', { method: 'DELETE', headers: { 'X-API-Key': 'ctk_your_key' } });

Webhooks

Receive real-time HTTP notifications when events happen in your account. (Enterprise plan)

Available Events

coral.created coral.updated coral.deleted coral.update.created

Signature Verification

Each webhook includes headers for verification:

# Headers sent with each webhook X-Webhook-Signature: <hmac_sha256> X-Webhook-Timestamp: <unix_timestamp> X-Webhook-Event: coral.created # Verify with: signature = hmac_sha256(secret, timestamp + '.' + json_body)

Error Handling

All errors follow a consistent JSON format:

{ "error": "Validation failed", "message": "The provided data is invalid", "errors": { "name": ["The name field is required."] } }

HTTP Status Codes

CodeMeaningWhen
200OKRequest successful
201CreatedNew coral created
401UnauthorizedMissing or invalid API key
403ForbiddenAPI disabled or coral limit reached
404Not FoundCoral doesn't exist
422Validation ErrorInvalid request data
429Rate LimitedToo many requests
500Server ErrorSomething went wrong

Downloads & Tools