analytics
Stateless API proxy for Google Analytics 4, Google Search Console,
Google Ads and Sklik (Seznam), running in AppFactory behind the Caddy
reverse proxy at /apps/analytics.
The service stores no secrets. Every credential is supplied per request as
an X- header and used only to call the upstream API.
Endpoints
Interactive docs (Swagger UI): /docs — publicly https://services.csbot.cz/apps/analytics/docs.
| Area | Method | Path |
|---|---|---|
| Meta | GET | /health, /version |
| GA4 Data | POST | /ga/data/properties/{id}/runReport |
| GA4 Data | POST | /ga/data/properties/{id}/runPivotReport |
| GA4 Data | POST | /ga/data/properties/{id}/batchRunReports |
| GA4 Data | POST | /ga/data/properties/{id}/batchRunPivotReports |
| GA4 Data | POST | /ga/data/properties/{id}/runRealtimeReport |
| GA4 Data | POST | /ga/data/properties/{id}/checkCompatibility |
| GA4 Data | GET | /ga/data/properties/{id}/metadata |
| GA4 Admin | GET | /ga/admin/accounts, /ga/admin/accountSummaries |
| GA4 Admin | GET | /ga/admin/properties (?accountId=), /ga/admin/properties/{id} |
| GA4 Admin | GET | /ga/admin/properties/{id}/dataStreams |
| Search Console | POST | /gsc/searchAnalytics/query (?siteUrl=), /gsc/urlInspection |
| Search Console | GET | /gsc/sites, /gsc/site, /gsc/sitemaps, /gsc/sitemap (?siteUrl=) |
| Google Ads | POST | /googleads/customers/{id}/search, /googleads/customers/{id}/searchStream |
| Google Ads | GET | /googleads/customers:listAccessibleCustomers |
| Sklik | POST | /sklik/login, /sklik/report/{entity}, /sklik/rpc/{method} |
| Sklik | GET | /sklik/limits, /sklik/write-limits |
| Sklik read | GET | /sklik/campaigns, /sklik/groups, /sklik/ads |
| Sklik write | POST | /sklik/{campaigns|groups|ads} — create, always paused |
| Sklik write | PUT | /sklik/{campaigns|groups|ads} — update by id (partial) |
| Sklik write | DELETE | /sklik/{campaigns|groups|ads}?ids= — remove (reversible) |
| Sklik write | POST | /sklik/{campaigns|groups|ads}/restore?ids= |
Credentials (headers)
Google Analytics — token wins over service account:
| Header | Required | Meaning |
|---|---|---|
X-GA-Access-Token |
one of these | Ready OAuth2 access token (used as Bearer). |
X-GA-Credentials |
one of these | Base64-encoded service-account JSON key; the proxy mints a token. |
X-GA-Quota-Project |
no | Google Cloud project id for quota/billing. |
Google Search Console — same model, prefix X-GSC- (scope webmasters.readonly):
X-GSC-Access-Token / X-GSC-Credentials (+ X-GSC-Quota-Project).
Google Ads — same OAuth (scope adwords) plus a developer token:
| Header | Required | Meaning |
|---|---|---|
X-GAds-Developer-Token |
yes | Google Ads developer token. |
X-GAds-Access-Token |
one of these | Ready OAuth2 access token. |
X-GAds-Credentials |
one of these | Base64 service-account JSON (needs domain-wide delegation). |
X-GAds-Login-Customer-Id |
no | Manager (MCC) id → login-customer-id. |
X-GAds-Quota-Project |
no | Google Cloud project id. |
Sklik:
| Header | Required | Meaning |
|---|---|---|
X-Sklik-Token |
yes | Sklik API token from account settings. |
X-Sklik-User-Id |
no | Managed account id for agency/MCC access. |
X-Idempotency-Key |
no | Writes only. A retry with the same key replays the original result instead of creating a duplicate. |
Sklik writes create everything paused (
status: suspend) — Sklik's own default isactive, so this is forced server-side.PUTis ordinary CRUD and can resume a campaign; setSKLIK_BLOCK_ACTIVATION=trueto forbid that. Budget ceilings are off by default; enable them withSKLIK_MAX_*.DELETEis Sklik's soft delete and is reversible via/restore. See documentation/sklik.md.
Where to obtain each credential is described at the top of /docs (Swagger) and
in documentation/.
Run locally
pip install -r requirements.txt
uvicorn app.main:app --reload --port 8000
# open http://localhost:8000/docs
Configuration (env)
Non-secret only — see app/config.py: ROOT_PATH,
GA_DATA_BASE_URL, GA_ADMIN_BASE_URL, GA_SCOPE, GSC_DATA_BASE_URL,
GSC_INSPECT_BASE_URL, GSC_SCOPE, GOOGLE_ADS_BASE_URL,
GOOGLE_ADS_API_VERSION, GOOGLE_ADS_SCOPE, SKLIK_BASE_URL,
SKLIK_LIST_PAGE_LIMIT, SKLIK_LIST_MAX_PAGES, HTTP_TIMEOUT_SECONDS,
LOG_LEVEL.
Sklik write guard rails (all optional, all off by default):
SKLIK_MAX_DAY_BUDGET_HALERS, SKLIK_MAX_TOTAL_BUDGET_HALERS,
SKLIK_MAX_CPC_HALERS (0 = no ceiling), SKLIK_IDEMPOTENCY_TTL_SECONDS,
SKLIK_BLOCK_ACTIVATION (default false),
SKLIK_RPC_ALLOW_MUTATIONS (default true).
See documentation/ for per-integration detail.