Skip to main content
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 https://api.linkrunner.io/api/v1/reporting/campaigns
Do not call the docs page URL (/api-reference/reporting-campaigns) as the API endpoint. The API path is /api/v1/reporting/campaigns.

Example request

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'

Response

{
    "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"
    }
}
pagination.total is the number of campaigns matching your filters, and pages is total / limit rounded up.
Numeric fields are returned as formatted strings ("3,201", "$12,540.50"). Strip the formatting before doing math: Number(value.replace(/[^0-9.-]/g, "")).

Query parameters

ParameterTypeDefaultNotes
from, tostring(none)YYYY-MM-DD, inclusive, in your project’s timezone. start_date / end_date are accepted aliases. Sets the metrics window; never filters the campaign list (see note below).
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.
from and to choose the window over which metrics (clicks, installs, revenue, spend) are computed. They do not filter which campaigns are returned: a campaign created after to still appears, with zeros for the window. This matches the dashboard campaign table, where the date picker changes the numbers, not the list. To fetch only campaigns created in a range, see Troubleshooting.
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 -G 'https://api.linkrunner.io/api/v1/reporting/campaigns' \
  -H 'linkrunner-key: YOUR_API_KEY' \
  --data-urlencode 'events=Purchase,AddToCart' \
  --data-urlencode 'unique_user_events=Purchase' \
  --data-urlencode 'cost_per_event=Purchase' \
  --data-urlencode 'active_users=14' \
  --data-urlencode 'from=2026-04-01' \
  --data-urlencode 'to=2026-04-30' \
  --data-urlencode 'limit=20'
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.

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.

Troubleshooting

This is expected. from and to set the metrics window; they never remove campaigns from the list. To keep only campaigns created in a range, request sort_field=created_at&sort_order=descending, then drop rows client-side once created_at falls before from.
The limit is 1 request per minute per API key. Honor the Retry-After: 60 header and cache responses for at least 60 seconds. The underlying analytics refresh on roughly the same cadence, so polling faster returns the same data.
Compare the same date range and the same view (user_acquisition vs retargeting). The API interprets from and to in your project’s timezone, exactly like the dashboard date picker. If you are doing math on the values, remember they are formatted strings (see the warning under Response).

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 };
}
Need help? Contact support@linkrunner.io