Skip to content

API reference

Every endpoint, parameter and report column of the PortyLink API. Paths are under /api/v1 on your app's address. For a walkthrough with examples, see the REST API guide; for tools, import openapi.json.

Authentication#

Every request needs Authorization: Bearer <key>. An API key such as plk_live_.... All endpoints are read-only GET requests that return JSON.

Endpoints#

Sites this key can read#

GET/api/v1/sites

StatusResponse
200Sites with the providers each has linked.
401Missing, invalid, revoked or expired API key.

Available reports#

GET/api/v1/reports

StatusResponse
200The report catalog.
401Missing, invalid, revoked or expired API key.

Run a report for a site#

GET/api/v1/sites/{siteId}/reports/{report}

ParameterInTypeDescription
siteIdrequiredpathstring, uuid
reportrequiredpathstring, one of 10See reports
rangequerylast_7_days | last_28_days | last_90_days | last_365_daysA preset ending yesterday in the source's time zone. Ignored when start and end are given. Default last_28_days.
startquerystring, dateYYYY-MM-DD, with end.
endquerystring, dateYYYY-MM-DD, with start.
limitqueryintegerRows per page; each report has its own maximum.
offsetqueryintegerUse meta.paging.nextOffset from the previous page.
StatusResponse
200Rows as objects keyed by column, totals for by-day reports, and metadata on the date range, freshness and paging.
400The request is invalid, e.g. a bad date range.
401Missing, invalid, revoked or expired API key.
403The key may not read this provider.
404Unknown site, or the site has no source linked for this report.
429Rate limited by PortyLink or by Google's quota. Retry after retryAfter seconds.
503Google couldn't be reached or the connected account lost access.

Reports#

Each row in data.rows is an object keyed by these columns: the dimension first, then the metrics. Rates are fractions (0.0452 means 4.52%).

gsc.overview

Search ConsoleOne row per day, with totals

Search performance by day. Clicks, impressions, CTR and average position from Google Search, per day, with totals.

ColumnKindMeaning
datedimensionDay, YYYY-MM-DD
clicksmetricClicks from Google Search
impressionsmetricTimes shown in results
ctrmetricClick-through rate, a fraction
positionmetricAverage position, one decimal

gsc.queries

Search Console25 rows by default, up to 1000

Top search queries. The searches the site appeared for, by clicks.

ColumnKindMeaning
querydimensionSearch query
clicksmetricClicks from Google Search
impressionsmetricTimes shown in results
ctrmetricClick-through rate, a fraction
positionmetricAverage position, one decimal

gsc.pages

Search Console25 rows by default, up to 1000

Top pages in search. The site's pages that appeared in Google Search, by clicks.

ColumnKindMeaning
pagedimensionPage URL
clicksmetricClicks from Google Search
impressionsmetricTimes shown in results
ctrmetricClick-through rate, a fraction
positionmetricAverage position, one decimal

gsc.countries

Search Console25 rows by default, up to 250

Search performance by country. Clicks and impressions by searcher country (ISO 3166-1 alpha-3).

ColumnKindMeaning
countrydimensionCountry, ISO 3166-1 alpha-3
clicksmetricClicks from Google Search
impressionsmetricTimes shown in results
ctrmetricClick-through rate, a fraction
positionmetricAverage position, one decimal

gsc.devices

Search Console10 rows by default, up to 10

Search performance by device. Clicks and impressions on desktop, mobile and tablet.

ColumnKindMeaning
devicedimensionDESKTOP, MOBILE or TABLET
clicksmetricClicks from Google Search
impressionsmetricTimes shown in results
ctrmetricClick-through rate, a fraction
positionmetricAverage position, one decimal

ga4.overview

AnalyticsOne row per day, with totals

Traffic by day. Active users, sessions, engaged sessions, engagement rate, views and key events, per day, with totals.

ColumnKindMeaning
datedimensionDay, YYYY-MM-DD
activeUsersmetricActive users
sessionsmetricSessions
engagedSessionsmetricEngaged sessions
engagementRatemetricEngaged sessions ÷ sessions, a fraction
screenPageViewsmetricViews
keyEventsmetricKey events (conversions)

ga4.channels

Analytics25 rows by default, up to 100

Traffic by channel. Sessions and users by default channel group (Organic Search, Direct, Paid Search, ...).

ColumnKindMeaning
sessionDefaultChannelGroupdimensionDefault channel group
sessionsmetricSessions
activeUsersmetricActive users
engagementRatemetricEngaged sessions ÷ sessions, a fraction
keyEventsmetricKey events (conversions)

ga4.sources

Analytics25 rows by default, up to 1000

Traffic by source / medium. Sessions and users by source and medium, e.g. google / organic.

ColumnKindMeaning
sessionSourceMediumdimensionSource / medium
sessionsmetricSessions
activeUsersmetricActive users
engagementRatemetricEngaged sessions ÷ sessions, a fraction
keyEventsmetricKey events (conversions)

ga4.landing_pages

Analytics25 rows by default, up to 1000

Top landing pages. Pages where sessions started, by sessions.

ColumnKindMeaning
landingPagePlusQueryStringdimensionLanding page with query string
sessionsmetricSessions
activeUsersmetricActive users
engagementRatemetricEngaged sessions ÷ sessions, a fraction
keyEventsmetricKey events (conversions)

ga4.pages

Analytics25 rows by default, up to 1000

Top pages. Pages by views, with users and total engagement time (seconds).

ColumnKindMeaning
pagePathdimensionPage path
screenPageViewsmetricViews
activeUsersmetricActive users
userEngagementDurationmetricTotal engagement time, seconds

Errors#

Errors return { "error": { "code", "message", "retryAfter"? } }. Branch on code, one of:

invalidunauthenticatedforbiddennot_foundconflictrate_limitedunavailableinternal

retryAfter (also sent as the Retry-After header) says how many seconds to wait. Status codes and their causes are in the guide.

Something wrong or unclear on this page?Email us