Skip to content

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:

json
{ "status": "ok" }

Example:

bash
curl https://api.tablrr.app/v1/health

Get 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 as pt, fr, or de. 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:

json
{
    "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)
  • ETag header for conditional requests
  • Vary: Authorization

Example:

bash
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=gb

Get 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 listing
  • country (query, optional): ISO 3166-1 alpha-2 country code. Filters operators by country when geo targeting is enabled.
  • locale (query, optional): Locale code such as pt, fr, or de. 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)
  • ETag header for conditional requests
  • Vary: Authorization

Example:

bash
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=de

Get 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)
  • ETag header for conditional requests
  • Vary: Authorization

Example:

bash
curl -H "Authorization: Bearer {token}" \
  https://api.tablrr.app/v1/listings/abc123/css

Get 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)
  • ETag header for conditional requests
  • Vary: Authorization

Example:

bash
curl -H "Authorization: Bearer {token}" \
  https://api.tablrr.app/v1/listings/abc123/js

Get 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 listing
  • country (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)
  • ETag header for conditional requests
  • Vary: Authorization

Example:

bash
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=gb

Track 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:

FieldTypeRequiredDescription
public_idstringYesThe listing's public ID
timestampintegerYesUnix timestamp (seconds) when the view occurred
json
{
    "public_id": "abc123xyz",
    "timestamp": 1737456789
}

Response:

  • 202 Accepted — event queued for processing
  • 403 Forbidden — token is not site-scoped
  • 422 Unprocessable Entity — validation failed

Example:

bash
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}'

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:

FieldTypeRequiredDescription
public_idstringYesThe listing's public ID
operator_idintegerYesThe operator's internal ID (from the listing HTML)
click_typestringYesEither "affiliate" or "terms"
timestampintegerYesUnix timestamp (seconds) when the click occurred
json
{
    "public_id": "abc123xyz",
    "operator_id": 42,
    "click_type": "affiliate",
    "timestamp": 1737456789
}

Response:

  • 202 Accepted — event queued for processing
  • 403 Forbidden — token is not site-scoped
  • 422 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:

javascript
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:

bash
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

javascript
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

html
<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

javascript
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);
    }
}