API Documentation
Build custom dashboards, automate reporting, or integrate analytics into your workflow.
Authentication
All API requests require authentication using a bearer token. You can create and manage API tokens from your dashboard.
Authorization: Bearer pk_live_a1b2c3d4e5f6...Token Format
Tokens follow the format pk_live_ followed by 32 random characters:
pk_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
└──┬──┘ └┬─┘ └─────────────┬─────────────┘
prefix env random (32 chars)Token Scopes
Tokens can be created with specific permissions:
| Scope | Description |
|---|---|
analytics:read | Read install analytics for your packages |
packages:read | List and view package details |
packages:write | Create, update, and delete packages |
Rate Limits
Currently free for everyone! While we're building out pkglnk, all users get Pro-level API limits. No restrictions, no catch.
Your current rate limit:
| Plan | Requests / Day |
|---|---|
| All Users (Free during development) | 1,000 |
Rate limit information is included in response headers:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 950
X-RateLimit-Reset: 1704931200Tracking Endpoint Limits
The /track/ proxy endpoint has separate rate limiting to prevent abuse and ensure
reliable analytics for all users:
| Limit | Value |
|---|---|
| Max requests per IP | 100 per hour |
When the limit is exceeded, analytics logging is paused but Git operations continue to work normally. This ensures package installs are never blocked, while protecting the analytics database from flooding attacks.
A typical package install generates 2-3 analytics events (clone_start, fetch_objects, and optionally LFS requests). The 100/hour limit allows approximately 30-50 normal installs per hour from a single IP.
URL Extensions
pkglnk supports URL extensions for monorepo packages and version pinning. These allow you to specify a subfolder path within a repository or pin to a specific branch or tag.
Subfolder Path
For monorepo packages where the Unity package lives in a subfolder, use the ?path= parameter:
https://github.com/owner/repo.git?path=Packages/com.example.packageThis maps to the git_path field in package objects.
Branch / Tag Reference
To pin to a specific branch or tag, use the # fragment:
# Pin to a specific tag
https://github.com/owner/repo.git#v1.0.0
# Pin to a branch
https://github.com/owner/repo.git#main
# Combined: path + ref
https://github.com/owner/repo.git?path=Packages/MyPackage#v2.0.0This maps to the git_ref field in package objects.
API Response Fields
When you create or retrieve packages, the URL extensions are returned as separate fields:
| Field | Description | Example |
|---|---|---|
git_path | Subfolder path within the repository | Packages/com.example.package |
git_ref | Git branch or tag reference | v1.0.0 or main |
Endpoints
Base URL: https://pkglnk.com/api/v1
/packagesList all packages owned by the authenticated user.
Required scope: packages:read
Response
{
"packages": [
{
"slug": "my-package",
"display_name": "My Package",
"git_platform": "github",
"git_owner": "username",
"git_repo": "repo-name",
"git_path": null,
"git_ref": "v1.0.0",
"is_private": false,
"total_installs": 1234,
"created_at": "2024-06-15T10:30:00Z"
}
],
"total": 1
}/packages/:slugGet details for a specific package.
Required scope: packages:read
Response
{
"slug": "my-package",
"display_name": "My Package",
"description": "A useful Unity package",
"git_platform": "github",
"git_owner": "username",
"git_repo": "repo-name",
"git_path": "Packages/com.example.package",
"git_ref": "main",
"is_private": false,
"total_installs": 1234,
"unique_ips": 890,
"created_at": "2024-06-15T10:30:00Z",
"updated_at": "2024-12-01T15:45:00Z"
}/packages/:slug/analyticsGet install analytics for a specific package.
Required scope: analytics:read
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
days | integer | 30 | Number of days to include (1-365) |
Example Request
curl -X GET "https://pkglnk.com/api/v1/packages/my-package/analytics?days=30" \
-H "Authorization: Bearer pk_live_a1b2c3d4..."Response
{
"package": {
"slug": "my-package",
"display_name": "My Package"
},
"period": {
"days": 30,
"start": "2024-12-08T00:00:00Z",
"end": "2025-01-07T23:59:59Z"
},
"totals": {
"installs": 1234,
"unique_ips": 890
},
"daily": [
{ "date": "2024-12-08", "installs": 45 },
{ "date": "2024-12-09", "installs": 52 }
],
"by_os": {
"windows": 612,
"macos": 489,
"linux": 133
},
"by_country": {
"US": 456,
"DE": 234,
"GB": 189
}
}/analytics/summaryGet aggregated analytics across all your packages.
Required scope: analytics:read
Response
{
"total_packages": 5,
"total_installs": 12450,
"total_unique_ips": 8920,
"top_packages": [
{ "slug": "popular-pkg", "installs": 5000 },
{ "slug": "another-pkg", "installs": 3200 }
]
}/packages/:slug/exportExport detailed install analytics data in JSON or CSV format for offline analysis or backup.
Required scope: analytics:read
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
format | string | json | Export format: json or csv |
days | integer | 30 | Number of days to include (1-365) |
Example Request
curl -X GET "https://pkglnk.com/api/v1/packages/my-package/export?format=json&days=30" \
-H "Authorization: Bearer pk_live_a1b2c3d4..."JSON Response
{
"package": {
"slug": "my-package",
"display_name": "My Package"
},
"export_info": {
"format": "json",
"days": 30,
"start_date": "2024-12-08T00:00:00.000Z",
"end_date": "2025-01-07T23:59:59.000Z",
"total_records": 156,
"exported_at": "2025-01-07T12:30:00.000Z"
},
"installs": [
{
"id": "abc123",
"timestamp": "2025-01-07T10:15:30.000Z",
"country_code": "US",
"city": "San Francisco",
"os": "windows",
"git_version": "2.43.0",
"event_type": "clone_start",
"subpath": null,
"session_id": "sess_xyz789"
}
]
}CSV Response
When format=csv, returns a downloadable CSV file with headers:
id,timestamp,country_code,city,os,git_version,event_type,subpath,session_idError Handling
The API uses standard HTTP status codes and returns errors in a consistent format:
{
"error": {
"code": "unauthorized",
"message": "Invalid or expired API token"
}
}Status Codes
| Code | Description |
|---|---|
200 | Success |
400 | Bad request - invalid parameters |
401 | Unauthorized - missing or invalid token |
403 | Forbidden - insufficient scope |
404 | Not found - package doesn't exist |
429 | Rate limit exceeded |
500 | Internal server error |
README Badges
Display live install counts in your README or website. Badges are publicly accessible and don't require authentication.
<img src="https://pkglnk.com/badge/your-package.svg" alt="Install count" />Badge Styles
Choose a badge style using the ?style= parameter:
pkglnk/badge/pkg.svgupm/badge/pkg.svg?style=upmupm-installs/badge/pkg.svg?style=upm-installsinstalls/badge/pkg.svg?style=installsBadges are cached for 1 hour to reduce load. Private packages display a "private" badge instead of the install count.