Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.linkrunner.io/llms.txt

Use this file to discover all available pages before exploring further.

The Reporting API returns the same campaign analytics shown on the Linkrunner dashboard.

Base URL

https://api.linkrunner.io/api/v1

Authentication

Pass your project’s server key in the linkrunner-key header. This is the same key used by other /api/v1 endpoints. Find it under Settings → Data APIs:
linkrunner-key: YOUR_API_KEY

Postman collection

Download the Postman collection. Import it into Postman, set the linkrunner_key collection variable to your key, and run any request.

Rate limit and freshness

  • 1 request per minute per API key (429 with Retry-After: 60 when exceeded).
  • 30 req/sec per source IP (shared across all /api/v1 endpoints).
  • Underlying analytics refresh on roughly the same cadence, so cache responses for at least 60 seconds.

Endpoint

GET /reporting/campaigns

Query parameters

ParameterTypeDefaultNotes
from, tostring(none)YYYY-MM-DD, project timezone. start_date / end_date are accepted aliases.
activestring(all)true or false.
networkstring(all)meta, google, apple_search_ads, tiktok, snapchat, sandbox_ads, etc.
meta_account_idnumber(none)Restrict to a single Meta network account.
platformstring(both)ios or android.
searchstring(none)Matches name, display ID, domain, deeplink, or full tracking URL.
viewstringuser_acquisitionuser_acquisition (new users: installs, cost_per_install) or retargeting (re-engaged users: reinstalls, reengagements, cost_per_reinstall).
sort_fieldstringinstallsclicks, installs, signups, revenue, spend, created_at, uninstalls, conversion, suspicious_installs, roas.
sort_orderstringdescendingascending or descending.
pagenumber11-indexed.
limitnumber10Max 100. Larger values return 422.
See Configurable columns below for events, payment_events, unique_user_events, cost_per_event, and active_users.

Configurable columns

By default the response includes:
  • All custom events your project tracks (under custom_events), each as a total occurrence count.
  • All payment events (under payment_events), each as a total count and total amount.
  • active_users with a 7-day window.
These query params let you narrow or change that:
ParameterEffectExample
eventsWhitelist custom_events to only these names. Smaller payload, faster query.events=Purchase,AddToCart
payment_eventsWhitelist payment_events to only these types.payment_events=PURCHASE
unique_user_eventsCount distinct users (unique_count) for these events instead of total occurrences. Works for both custom and payment events.unique_user_events=Purchase
cost_per_eventInclude cost_per_custom_events / cost_per_payment_events for these event names. Off by default.cost_per_event=Purchase,PURCHASE
active_usersOverride the active-users window (in days).active_users=14
All five accept a comma-separated list. Unknown event names are silently dropped (they map to no rows in our analytics store). Find your project’s event names under Dashboard → Settings → Events.

Example: revenue events with cost-per and 14-day active users

curl 'https://api.linkrunner.io/api/v1/reporting/campaigns?\
events=Purchase,AddToCart&\
unique_user_events=Purchase&\
cost_per_event=Purchase&\
active_users=14&\
from=2026-04-01&to=2026-04-30&limit=20' \
  -H 'linkrunner-key: YOUR_API_KEY'
Returns each campaign with:
  • custom_events.Purchase containing a unique_count (distinct paying users) instead of count.
  • custom_events.AddToCart with the standard total count.
  • cost_per_custom_events.Purchase (spend ÷ Purchase count).
  • active_users computed over a 14-day window.

Example

curl 'https://api.linkrunner.io/api/v1/reporting/campaigns?from=2026-04-01&to=2026-04-30&network=meta&limit=20' \
  -H 'linkrunner-key: YOUR_API_KEY'
{
    "msg": "Campaigns fetched successfully",
    "status": 200,
    "data": {
        "campaigns": [ /* see TypeScript types below */ ],
        "pagination": { "total": 142, "pages": 8, "page": 1, "limit": 20 },
        "display_currency": "USD",
        "view": "user_acquisition"
    }
}
Numeric fields are returned as formatted strings ("3,201", "$12,540.50"). Strip commas before parsing for math: Number(value.replace(/[^0-9.-]/g, "")).

Errors

StatusWhen
401Missing or invalid linkrunner-key.
402Billing account suspended. Body includes payment_link.
422limit is non-numeric, less than 1, or greater than 100.
429Rate limit exceeded.
500Unexpected server error. Retry after a short backoff.

TypeScript types

export interface ReportingCampaignsResponse {
    msg: string;
    status: number;
    data: {
        campaigns: Campaign[];
        pagination: { total: number; pages: number; page: number; limit: number };
        display_currency?: string;
        view?: "user_acquisition" | "retargeting";
    };
}

export interface Campaign {
    id: number;
    active: boolean;
    created_at: string;
    display_id: string;
    link: string;
    name: string;

    // Counts (formatted strings)
    clicks: string;
    installs: string;
    reinstalls?: string;
    reengagements?: string;
    "sign-ups": string;
    uninstalls?: string;
    suspicious_installs?: string;
    click_through_attribution?: string;
    view_through_attribution?: string;
    conversion: string;

    // Money (formatted strings, in display_currency)
    revenue: string;
    revenue_event_count?: string;
    spend: string;
    roas?: string;
    cost_per_install?: string;
    cost_per_reinstall?: string;
    cost_per_signup?: string;

    // Network flags
    meta: boolean;
    meta_web_to_app: boolean;
    google: boolean;
    google_web_to_app: boolean;
    tiktok?: boolean;
    snapchat?: boolean;
    linkedin?: boolean;
    ad_network_code?: string;

    // Platforms
    ios: boolean;
    android: boolean;

    // Misc
    activity_in_last_x_days: number;
    has_date_filter: boolean;
    domain?: { id: number; name: string } | null;
    custom_store_listing: string | null;
    active_users?: number | string;

    // Custom & payment events: map of event name → count/amount
    custom_events?: { [name: string]: { count: string; amount?: string; unique_count?: string } | string };
    payment_events?: { [type: string]: { count: string; total: string } };
    cost_per_custom_events?: { [name: string]: string };
    cost_per_payment_events?: { [type: string]: string };

    // Retention buckets
    retention: { d1: number | string; d7: number | string };
    classic_retention?: { d1?: number | string; d7?: number | string; d14?: number | string; d30?: number | string };
    daywise_revenue?: { d0?: number | string; d3?: number | string; d7?: number | string; d30?: number | string };

    // Connected ad-account (Meta / Google / Apple Search Ads)
    network_account?: {
        id: number;
        name: string;
        email: string;
        status: string;
        capi_configured: boolean;
        account_name?: string;
        external_customer_id?: string;
    } | null;

    // Hierarchy: campaign → adSets → adCreatives + keywords
    adSets?: AdSet[];
}

export interface AdSet {
    id: string;
    name: string;
    clicks: string;
    installs: string;
    "sign-ups": string;
    spend: string;
    revenue: string;
    revenue_event_count?: string;
    conversion: string;
    roas?: string;
    cost_per_install?: string;
    cost_per_signup?: string;
    suspicious_installs?: string;
    custom_events?: Campaign["custom_events"];
    payment_events?: Campaign["payment_events"];
    cost_per_custom_events?: { [name: string]: string };
    cost_per_payment_events?: { [type: string]: string };
    daywise_revenue?: Campaign["daywise_revenue"];
    ios: boolean;
    android: boolean;
    retention: { d1: number | string; d7: number | string };
    adCreatives?: AdCreative[];
    keywords?: Keyword[];
}

export interface AdCreative {
    id: string;
    name: string;
    type: string;
    deeplink: number;
    clicks: string;
    installs: string;
    "sign-ups": string;
    spend: string;
    revenue: string;
    revenue_event_count?: string;
    conversion: string;
    roas?: string;
    cost_per_install?: string;
    cost_per_signup?: string;
    custom_events?: Campaign["custom_events"];
    payment_events?: Campaign["payment_events"];
    cost_per_custom_events?: { [name: string]: string };
    cost_per_payment_events?: { [type: string]: string };
    retention: { d1: number | string; d7: number | string };
}

// Apple Search Ads keyword-level rows. Same shape as AdCreative metrics.
export interface Keyword {
    id: string;
    name: string;
    clicks: string;
    installs: string;
    "sign-ups": string;
    spend: string;
    revenue: string;
    revenue_event_count?: string;
    conversion: string;
    roas?: string;
    cost_per_install?: string;
    cost_per_signup?: string;
    suspicious_installs?: string;
    custom_events?: Campaign["custom_events"];
    payment_events?: Campaign["payment_events"];
    cost_per_custom_events?: { [name: string]: string };
    cost_per_payment_events?: { [type: string]: string };
    retention?: { d1: number | string; d7: number | string };
}