Core API
These are the endpoints your site calls at runtime — fetching listing content to render on each page load, and reporting analytics events as visitors interact with listings.
Health Check
Check if the API is available.
Endpoint: GET /health
Authentication: Not required
Response:
{ "status": "ok" }Example:
curl https://api.tablrr.app/v1/healthGet Listing Embed Data
Get everything needed to embed a listing: HTML, CSS, JavaScript, structured data, and direct URLs to each asset. This is the easiest way to integrate a listing.
Endpoint: GET /listings/{publicId}/embed
Authentication: Required
Parameters:
publicId(path, required): The public ID of your listing (found in the listing URL or settings)country(query, optional): ISO 3166-1 alpha-2 country code (e.g.gb,de). When geo targeting is enabled, only operators visible to visitors from this country are included in the HTML and structured data. CSS and JS are not affected. Omit to receive all operators regardless of geo rules.locale(query, optional): Locale code such aspt,fr, orde. When provided, tablrr renders operators in that language. Takes priority over the listing's selected locale. Falls back to original content for untranslated fields.
Response:
{
"html": "<div>...</div>",
"css": "/* CSS styles */",
"js": "/* JavaScript code */",
"structured_data": "/* JSON data */",
"html_url": "https://api.tablrr.app/v1/listings/{publicId}/html",
"css_url": "https://api.tablrr.app/v1/listings/{publicId}/css",
"js_url": "https://api.tablrr.app/v1/listings/{publicId}/js",
"structured_data_url": "https://api.tablrr.app/v1/listings/{publicId}/structured_data",
"updated_at": "2025-01-10T10:00:00Z"
}Caching:
Cache-Control: private, max-age=3600(1 hour)ETagheader for conditional requestsVary: Authorization
Example:
curl -H "Authorization: Bearer {token}" \
https://api.tablrr.app/v1/listings/abc123/embed
# With translations
curl -H "Authorization: Bearer {token}" \
https://api.tablrr.app/v1/listings/abc123/embed?locale=pt
# With geo targeting
curl -H "Authorization: Bearer {token}" \
https://api.tablrr.app/v1/listings/abc123/embed?country=gbGet Listing HTML
Get the HTML content for a listing.
Endpoint: GET /listings/{publicId}/html
Authentication: Required
Parameters:
publicId(path, required): The public ID of your listingcountry(query, optional): ISO 3166-1 alpha-2 country code. Filters operators by country when geo targeting is enabled.locale(query, optional): Locale code such aspt,fr, orde. Renders translated operator content when available, with automatic fallback.
Response: HTML content with Content-Type: text/html; charset=UTF-8
Caching:
Cache-Control: private, max-age=3600(1 hour)ETagheader for conditional requestsVary: Authorization
Example:
curl -H "Authorization: Bearer {token}" \
https://api.tablrr.app/v1/listings/abc123/html
# With translations
curl -H "Authorization: Bearer {token}" \
https://api.tablrr.app/v1/listings/abc123/html?locale=de
# With geo targeting
curl -H "Authorization: Bearer {token}" \
https://api.tablrr.app/v1/listings/abc123/html?country=deGet Listing CSS
Get the CSS styles for a listing.
Endpoint: GET /listings/{publicId}/css
Authentication: Required
Parameters:
publicId(path, required): The public ID of your listing
Response: CSS content with Content-Type: text/css; charset=UTF-8
Caching:
Cache-Control: private, max-age=3600(1 hour)ETagheader for conditional requestsVary: Authorization
Example:
curl -H "Authorization: Bearer {token}" \
https://api.tablrr.app/v1/listings/abc123/cssGet Listing JavaScript
Get the JavaScript for a listing.
Endpoint: GET /listings/{publicId}/js
Authentication: Required
Parameters:
publicId(path, required): The public ID of your listing
Response: JavaScript content with Content-Type: application/javascript; charset=UTF-8
Caching:
Cache-Control: private, max-age=3600(1 hour)ETagheader for conditional requestsVary: Authorization
Example:
curl -H "Authorization: Bearer {token}" \
https://api.tablrr.app/v1/listings/abc123/jsGet Listing Structured Data
Get the Schema.org ItemList JSON-LD structured data for a listing. Inject into the <head> of your page for SEO.
Endpoint: GET /listings/{publicId}/structured_data
Authentication: Required
Parameters:
publicId(path, required): The public ID of your listingcountry(query, optional): ISO 3166-1 alpha-2 country code. Filters operators by country when geo targeting is enabled.
Response: JSON-LD content with Content-Type: application/json; charset=UTF-8
Caching:
Cache-Control: private, max-age=3600(1 hour)ETagheader for conditional requestsVary: Authorization
Example:
curl -H "Authorization: Bearer {token}" \
https://api.tablrr.app/v1/listings/abc123/structured_data
# With geo targeting
curl -H "Authorization: Bearer {token}" \
https://api.tablrr.app/v1/listings/abc123/structured_data?country=gbTrack a Listing View
Report that a listing was displayed to a visitor. Use this when rendering listings via the direct API to populate analytics in your tablrr dashboard.
Note: The WordPress plugin sends view events automatically. This endpoint is only needed for custom direct-API integrations.
Endpoint: POST /analytics/views
Authentication: Required (site-scoped token)
Rate limit: 2,000 requests per minute
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
public_id | string | Yes | The listing's public ID |
timestamp | integer | Yes | Unix timestamp (seconds) when the view occurred |
{
"public_id": "abc123xyz",
"timestamp": 1737456789
}Response:
202 Accepted— event queued for processing403 Forbidden— token is not site-scoped422 Unprocessable Entity— validation failed
Example:
curl -X POST https://api.tablrr.app/v1/analytics/views \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{"public_id": "abc123xyz", "timestamp": 1737456789}'Track a Link Click
Report that a visitor clicked an affiliate or terms link within a listing.
Note: The WordPress plugin sends click events automatically. This endpoint is only needed for custom direct-API integrations.
Endpoint: POST /analytics/clicks
Authentication: Required (site-scoped token)
Rate limit: 2,000 requests per minute
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
public_id | string | Yes | The listing's public ID |
operator_id | integer | Yes | The operator's internal ID (from the listing HTML) |
click_type | string | Yes | Either "affiliate" or "terms" |
timestamp | integer | Yes | Unix timestamp (seconds) when the click occurred |
{
"public_id": "abc123xyz",
"operator_id": 42,
"click_type": "affiliate",
"timestamp": 1737456789
}Response:
202 Accepted— event queued for processing403 Forbidden— token is not site-scoped422 Unprocessable Entity— validation failed (e.g. operator does not belong to this listing)
Getting the operator ID from listing HTML:
The listing HTML includes data-operator-id attributes on each row or card. Read this value when a link is clicked:
link.addEventListener('click', () => {
const row = link.closest('[data-operator-id]');
const operatorId = parseInt(row.dataset.operatorId, 10);
const listingId = link.closest('[data-listing-id]').dataset.listingId;
const clickType = link.dataset.linkType; // "affiliate" or "terms"
fetch('https://api.tablrr.app/v1/analytics/clicks', {
method: 'POST',
headers: {
Authorization: 'Bearer {token}',
'Content-Type': 'application/json',
},
body: JSON.stringify({
public_id: listingId,
operator_id: operatorId,
click_type: clickType,
timestamp: Math.floor(Date.now() / 1000),
}),
});
});Only send click events for links that have a data-link-type attribute.
Example:
curl -X POST https://api.tablrr.app/v1/analytics/clicks \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{"public_id": "abc123xyz", "operator_id": 42, "click_type": "affiliate", "timestamp": 1737456789}'Usage Examples
Option 1: Load Everything at Once
const response = await fetch(
'https://api.tablrr.app/v1/listings/abc123/embed',
{
headers: { Authorization: 'Bearer your-token-here' },
},
);
const data = await response.json();
document.getElementById('listing-container').innerHTML = data.html;
const style = document.createElement('style');
style.textContent = data.css;
document.head.appendChild(style);
const script = document.createElement('script');
script.textContent = data.js;
document.body.appendChild(script);
// Inject structured data for SEO
const structuredDataScript = document.createElement('script');
structuredDataScript.type = 'application/ld+json';
structuredDataScript.textContent = JSON.stringify(data.structured_data);
document.head.appendChild(structuredDataScript);Option 2: Load Assets Separately
<div id="listing-container"></div>
<link rel="stylesheet" href="https://api.tablrr.app/v1/listings/abc123/css" />
<script src="https://api.tablrr.app/v1/listings/abc123/js"></script>
<script>
fetch('https://api.tablrr.app/v1/listings/abc123/html', {
headers: { Authorization: 'Bearer your-token-here' },
})
.then((r) => r.text())
.then((html) => {
document.getElementById('listing-container').innerHTML = html;
});
</script>Handle Errors
const response = await fetch(
'https://api.tablrr.app/v1/listings/abc123/embed',
{ headers: { Authorization: 'Bearer your-token-here' } },
);
if (!response.ok) {
const error = await response.json();
switch (error.error.code) {
case 'RESOURCE_NOT_FOUND':
console.error('Listing not found. Check the public ID.');
break;
case 'FORBIDDEN':
if (error.error.message.includes('premium template')) {
console.error('Upgrade to Hobby plan or higher for this template.');
} else {
console.error('Upgrade to Growth plan or higher for direct API access.');
}
break;
case 'VALIDATION_FAILED':
console.error('Invalid request:', error.error.details);
break;
default:
console.error('An error occurred:', error.error.message);
}
}