Skip to main content

Base URL

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

Authentication

All API requests require authentication. Include your server key in the request headers.

Error Responses

  • 401 Unauthorized
    • "Unauthorized access." - Missing or invalid API key
  • 429 Too Many Requests
    • Message: "Rate limit exceeded. Please try again later."
    • Cause: You’ve exceeded the rate limit of 30 requests per second.

Rate Limit Details

  • Rate: 30 requests per second
  • Status code on limit exceeded: 429 (Too Many Requests)

Authentication

All API requests require authentication using an API key. You must include this key in the header of every request.

API Key Header

Include the following header in all API requests:
linkrunner-key: YOUR_API_KEY
Replace YOUR_API_KEY with your actual API key. (Refer instructions below)

Obtaining Your API Key

You can find your API key on the Linkrunner settings page:
  1. Go to https://dashboard.linkrunner.io/settings?s=data-apis
  2. Locate your Server key on this page
  3. Use this key in the linkrunner-key header for all API requests
Keep your API key confidential. Do not share it or expose it in client-side code. Always make API requests from a secure server-side environment.

Endpoints

1. List Campaigns

Retrieve a paginated list of campaigns with optional filtering by status or specific link.

Request

GET /campaigns

Query Parameters

ParameterTypeRequiredDescription
filterstringNoFilter campaigns by status. Options: ACTIVE, INACTIVE, ALL (default: ALL)
channelstringNoFilter campaigns by advertising channel/network. Options: GOOGLE, META, TIKTOK, APPLE_SEARCH_ADS, or any affiliate name (case-insensitive). For affiliates, use underscores for spaces (e.g., AFFILIATE_EXAMPLE for “affiliate example”)
domainstringNoFilter campaigns by domain name (e.g., app.example.com). Only returns campaigns associated with the specified domain.
linkstringNoFilter campaigns by a specific link URL. The system will extract the campaign from the provided link.
pagenumberNoThe page number to retrieve. Must be a positive integer. (default: 1)
limitnumberNoThe number of campaigns to return per page. Must be between 1 and 1000. (default: 100, max: 1000)

Channel Filter Behavior

ValueBehavior
GOOGLEReturns campaigns where google = true
METAReturns campaigns where meta = true
TIKTOKReturns campaigns linked to a TikTok ad network
APPLE_SEARCH_ADSReturns campaigns linked to Apple Search Ads ad network
AFFILIATE_EXAMPLEReturns campaigns linked to an affiliate named “affiliate example”. Replace spaces with underscores.
(empty/invalid)No channel filter applied (returns all channels)
Affiliate Channel Naming:
  • Affiliate names should use underscores instead of spaces
  • Example: If the affiliate is named “affiliate example”, use channel=AFFILIATE_EXAMPLE
  • Channel names are case-insensitive

Pagination

Pagination is automatically applied to all requests with the following defaults:
  • Default page: 1
  • Default limit: 100 campaigns per page
  • Maximum limit: 1000 campaigns per page

Example Requests

  1. Basic Request (returns first 100 campaigns):
GET /campaigns
  1. Filter Active Campaigns with Pagination:
GET /campaigns?filter=ACTIVE&page=2&limit=50
  1. Get Campaign by Link:
GET /campaigns?link=https://yourdomain.com?c=XYZ123
  1. All Campaigns with Custom Limit:
GET /campaigns?filter=ALL&limit=200
  1. Filter by Channel (Google):
GET /campaigns?channel=GOOGLE
  1. Filter by Channel (Meta):
GET /campaigns?channel=META
  1. Filter by Channel (TikTok):
GET /campaigns?channel=TIKTOK
  1. Combine Channel with Other Filters:
GET /campaigns?channel=google&filter=ACTIVE&page=1&limit=50

Response

Standard Response (no channel filter):
{
    "msg": "Successfully retrieved 10 campaigns (all campaigns). Showing page 1 of 4.",
    "status": 200,
    "data": {
        "total_campaigns": 10,
        "campaigns": [
            {
                "display_id": "XYZ123",
                "name": "Summer_Promo_2024",
                "created_at": "2024-09-30T09:37:06.989Z",
                "update_at": "2024-09-30T09:37:06.989Z",
                "google": false,
                "meta": true,
                "meta_campaign_id": "987654321098765432",
                "meta_web_to_app": false,
                "active": true,
                "default_link": true,
                "attributed_users": 491,
                "link": "https://yourdomain.com?c=XYZ123",
                "shareable_link": "https://yourdomain.com/XYZ123",
                "domain": "yourdomain.com",
                "store_listings": [
                    {
                        "store_listing_id": "ios-store-v1",
                        "name": "iOS Main Listing",
                        "platform": "IOS"
                    },
                    {
                        "store_listing_id": "android-promo-v2",
                        "name": "Android Promo Listing",
                        "platform": "ANDROID"
                    }
                ]
            }
            // ... more campaigns
        ],
        "pagination": {
            "total": 35,
            "pages": 4,
            "page": 1,
            "limit": 10
        }
    }
}

Error Responses

Status CodeError MessageDescription
400”Invalid request parameters.”Invalid link, domain not found, or campaign not found
400”Invalid domain ''. Domain not found for this project.”Specified domain does not exist or doesn’t belong to your project
400”Invalid channel ''. Channel not found.”Specified channel/ad network does not exist
401”Unauthorized access.”Missing or invalid API key
422”Invalid page number ''. Page must be a positive integer…”Page parameter is not a positive integer
422”Invalid limit ''. Limit must be between 1 and 1000…”Limit parameter is not between 1 and 1000
422”Limit exceeds maximum allowed value of 1000…”Limit exceeds 1000

TypeScript Types

interface StoreListing {
    store_listing_id: string;
    name: string;
    platform: "IOS" | "ANDROID";
}

interface Campaign {
    display_id: string;
    name: string;
    created_at: string;
    update_at: string;
    google: boolean;
    meta: boolean;
    meta_campaign_id: string | null;
    meta_web_to_app: boolean;
    active: boolean;
    default_link: boolean;
    attributed_users: number;
    link: string;
    shareable_link: string;
    domain: string | null;
    store_listings: StoreListing[];
}

interface PaginationInfo {
    total: number;
    pages: number;
    page: number;
    limit: number;
}

interface CampaignsResponse {
    msg: string;
    status: number;
    data: {
        total_campaigns: number;
        campaigns: Campaign[];
        pagination: PaginationInfo;
    };
}

2. Get Attributed Users

Retrieve a paginated list of users attributed to a specific campaign with optional time range filtering.

Request

GET /attributed-users

Query Parameters

ParameterTypeRequiredDescription
display_idstringYesThe display ID of the campaign to retrieve attributed users for
start_timestampstringNoStart time in ISO 8601 format (e.g., “2025-06-23T10:29:26.074”)
end_timestampstringNoEnd time in ISO 8601 format (e.g., “2025-06-23T10:29:26.074”)
timezonestringNoIANA timezone identifier. See IANA Time Zone Database (default: UTC)
pagenumberNoThe page number to retrieve. Must be a positive integer. (default: 1)
limitnumberNoThe number of users to return per page. Must be between 1 and 1000. (default: 50, max: 1000)

Pagination

Pagination is automatically applied to all requests with the following defaults:
  • Default page: 1
  • Default limit: 50 users per page
  • Maximum limit: 1000 users per page

Time Format Details

  • ISO 8601 Format: Use YYYY-MM-DDThh:mm:ss.SSS format (e.g., 2025-06-23T10:29:26.074)
  • IANA Timezone: Standard timezone identifiers like America/New_York, Asia/Tokyo, Asia/Kolkata
  • Default Timezone: UTC is used when timezone parameter is not provided
  • Time Range: Both start and end timestamps are optional; you can filter by start time only, end time only, or both

Example Requests

  1. Basic Request (returns first 50 users):
GET /attributed-users?display_id=XYZ123
  1. With Time Range and Timezone:
GET /attributed-users?display_id=XYZ123&start_timestamp=2025-06-23T10:29:26.074&end_timestamp=2025-06-24T10:29:26.074&timezone=Asia/Kolkata
  1. With Pagination:
GET /attributed-users?display_id=XYZ123&page=2&limit=100
  1. With All Parameters:
GET /attributed-users?display_id=XYZ123&start_timestamp=2025-06-23T10:29:26.074&end_timestamp=2025-06-24T10:29:26.074&timezone=America/New_York&page=3&limit=200

Response

{
    "msg": "Successfully retrieved 123 attributed users for campaign XYZ123. Showing page 1 of 3.",
    "status": 200,
    "data": {
        "total_users": 50,
        "users": [
            {
                "attributed_at": "2024-09-23T08:13:34.417Z",
                "ad_channel": "META",
                "campaign_display_id": "XYZ123",
                "campaign_name": "Summer_Promo_2024",
                "link": null,
                "installed_at": "2024-08-12T20:59:03.000Z",
                "store_click_at": "2024-08-12T20:58:25.000Z",
                "meta_ad_id": "138726495012847390",
                "ad_creative_id": "138726495084763210",
                "ad_creative_name": "Variation_8",
                "ad_set_id": "138726384957162840",
                "ad_set_name": "Engagement",
                "publisher_platform": "instagram",
                "platform_position": null,
                "user_data": {
                    "id": "user123456789",
                    "name": "John Smith",
                    "email": "[email protected]",
                    "phone": "+1234567890",
                    "device_data": {
                        "brand": "Samsung",
                        "base_os": "",
                        "version": "0.7.6",
                        "build_id": "XYZ123.456",
                        "api_level": 33,
                        "bundle_id": "com.example.app",
                        "device_id": "DEVICE123456",
                        "android_id": "ANDROID987654",
                        "device_name": "Galaxy S21",
                        "device_type": "user",
                        "build_number": "2219",
                        "connectivity": "Wi-Fi",
                        "manufacturer": "Samsung",
                        "application_name": "ExampleApp"
                    }
                }
            }
            // ... more users
        ],
        "pagination": {
            "total": 123,
            "pages": 3,
            "page": 1,
            "limit": 50
        }
    }
}

Error Responses

Status CodeError MessageDescription
400”Invalid timezone format.”Invalid IANA timezone identifier provided
400”Invalid timestamp format.”Invalid start or end timestamp format
401”Authentication failed.”Invalid or missing API key
404”Resource not found.”Campaign does not exist in the project
422”Required parameter missing.”Missing required display_id parameter
422”Invalid page number ''. Page must be a positive integer…”Page parameter is not a positive integer
422”Invalid limit ''. Limit must be between 1 and 1000…”Limit parameter is not between 1 and 1000
422”Limit exceeds maximum allowed value of 1000…”Limit exceeds 1000

TypeScript Types

interface DeviceData {
    brand: string;
    base_os: string;
    version: string;
    build_id: string;
    api_level: number;
    bundle_id: string;
    device_id: string;
    android_id: string;
    device_name: string;
    device_type: string;
    build_number: string;
    connectivity: string;
    manufacturer: string;
    application_name: string;
}

interface UserData {
    id: string | null;
    name: string | null;
    email: string | null;
    phone: string | null;
    device_data: DeviceData;
    // Additional user data fields may be included
    [key: string]: any;
}

interface AttributedUser {
    attributed_at: string;
    ad_channel: "GOOGLE" | "META" | "TIKTOK" | null;
    campaign_display_id: string;
    campaign_name: string;
    link: string | null;
    installed_at: string | null;
    store_click_at: string | null;
    meta_ad_id: string;
    ad_creative_id: string;
    ad_creative_name: string;
    ad_set_id: string;
    ad_set_name: string;
    publisher_platform: string | null;
    platform_position: string | null;
    user_data: UserData;
}

interface PaginationInfo {
    total: number;
    pages: number;
    page: number;
    limit: number;
}

interface AttributedUsersResponse {
    msg: string;
    status: number;
    data: {
        total_users: number;
        users: AttributedUser[];
        pagination: PaginationInfo;
    };
}

Campaign Domain and Store Listings

Domain Field

Each campaign response now includes a domain field that indicates which domain is being used for the campaign links:
  • Type: string | null
  • Description: The domain name used to generate campaign links
  • Behavior: If the campaign has a specific domain assigned, it will be shown. Otherwise, it falls back to the project’s primary domain.
  • Example: "yourdomain.com" or "promo.example.com"

Store Listings Array

Campaigns can now include multiple store listings in the response. Each store listing represents a different App Store or Play Store configuration. Store Listing Object Structure:
FieldTypeDescription
store_listing_idstringUnique identifier for the store listing
namestringDisplay name of the store listing
platform”IOS” | “ANDROID”Platform this store listing is for (iOS or Android)
Managing Store Listings:
  • Create and manage store listings in your Dashboard Settings
  • Each store listing has a unique store_listing_id that you use when creating campaigns
  • Store listings allow you to configure different App Store/Play Store parameters per campaign

3. Get Attribution Result

Retrieve attribution data for a specific user or device. This endpoint returns campaign and ad network information for attributed installs.

Request

GET /get-attribution-result

Query Parameters

ParameterTypeRequiredDescription
device_identifierstringConditionalThe unique device ID to look up. For Android, this is the GAID (Google Advertising ID). For iOS, this is the IDFA (Identifier for Advertisers). Required if user_id is not provided.
user_idstringConditionalThe user ID to look up. Required if device_identifier is not provided.
Note: At least one of device_identifier or user_id must be provided. If both are provided and they match different installs, the user_id based result will be returned as it’s more reliable.

Example Requests

  1. Get Attribution by Device Identifier:
GET /get-attribution-result?device_identifier=550e8400-e29b-41d4-a716-446655440000
  1. Get Attribution by User ID:
GET /get-attribution-result?user_id=user123456
  1. Get Attribution with Both Identifiers:
GET /get-attribution-result?device_identifier=550e8400-e29b-41d4-a716-446655440000&user_id=user123456

Response

Success Response (200):
{
    "msg": "Attribution result retrieved successfully.",
    "status": 200,
    "data": {
        "campaign_name": "Summer Sale 2024",
        "display_id": "XYZ123",
        "deeplink": "https://app.domain.com/promo/summer",
        "domain_name": "app.domain.com",
        "ad_set_id": "138726384957162840",
        "ad_set_name": "Engagement Campaign",
        "ad_creative_id": "138726495084763210",
        "ad_creative_name": "Creative Variation 1",
        "keyword_id": "",
        "keyword_name": "",
        "ad_network": "META",
        "attribution_type": "INORGANIC"
    }
}
No Attribution Data (204): If no attribution data is found for the provided identifier, the API returns a 204 No Content status with an empty response body.

Response Properties

PropertyTypeDescription
campaign_namestring|nullThe name of the campaign that the user was attributed to
display_idstring|nullThe campaign’s display identifier
deeplinkstring|nullThe deeplink URL associated with the campaign
domain_namestring|nullThe domain name used for the campaign
ad_set_idstring|nullThe ad set ID from the advertising platform (e.g., Meta Ad Set ID)
ad_set_namestring|nullThe ad set name from the advertising platform
ad_creative_idstring|nullThe ad creative ID from the advertising platform
ad_creative_namestring|nullThe ad creative name from the advertising platform
keyword_idstring|nullThe keyword ID (primarily for Apple Search Ads campaigns)
keyword_namestring|nullThe keyword name (primarily for Apple Search Ads campaigns)
ad_networkstring|nullThe advertising network. Can be GOOGLE, META, TIKTOK, APPLE_SEARCH_ADS, or other network codes
attribution_type”ORGANIC”|“INORGANIC”

Error Responses

Status CodeError MessageDescription
400”Either device_identifier or user_id is required.”Neither device_identifier nor user_id was provided
401”Unauthorized access.”Missing or invalid API key
204No ContentNo attribution data found for the provided identifier

TypeScript Types

type AdNetwork = "GOOGLE" | "META" | "TIKTOK" | "APPLE_SEARCH_ADS" | string;
type AttributionType = "ORGANIC" | "INORGANIC";

interface AttributionResult {
    campaign_name: string | null;
    display_id: string | null;
    deeplink: string | null;
    domain_name: string | null;
    ad_set_id: string | null;
    ad_set_name: string | null;
    ad_creative_id: string | null;
    ad_creative_name: string | null;
    keyword_id: string | null;
    keyword_name: string | null;
    ad_network: AdNetwork | null;
    attribution_type: AttributionType;
}

interface AttributionResultResponse {
    msg: string;
    status: number;
    data: AttributionResult;
}

Notes

  • All timestamps are in ISO 8601 format.
  • The device_data object contains detailed information about the user’s device.
  • The active field in the campaign data indicates whether the campaign is currently active.
  • The attributed_users field in the campaign data shows the number of users attributed to that campaign.
  • User data provided in this documentation is randomized for privacy reasons. Actual API responses will contain real user data.
  • The TypeScript types provided are based on the example responses
  • The domain field shows which domain is being used for campaign link generation
  • The store_listings array contains store listing configurations associated with the campaign
  • Store listings can be created and managed in the Dashboard Settings
  • The get-attribution-result endpoint returns a 204 status code with no body when no attribution data is found
For any help please reach out to [email protected]