104 lines
4.9 KiB
Markdown
104 lines
4.9 KiB
Markdown
# 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/keywords` |
|
|
| Sklik **write** | POST | `/sklik/{campaigns\|groups\|ads\|keywords}` — create (campaigns/groups/ads always **paused**) |
|
|
| Sklik **write** | PUT | `/sklik/{campaigns\|groups\|ads\|keywords}` — update by id (partial) |
|
|
| Sklik **write** | DELETE | `/sklik/{campaigns\|groups\|ads\|keywords}?ids=` — remove (reversible) |
|
|
| Sklik **write** | POST | `/sklik/{campaigns\|groups\|ads\|keywords}/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 is `active`, so this is forced server-side. `PUT` is ordinary CRUD and
|
|
> *can* resume a campaign; set `SKLIK_BLOCK_ACTIVATION=true` to forbid that.
|
|
> Budget ceilings are **off by default**; enable them with `SKLIK_MAX_*`.
|
|
> `DELETE` is Sklik's soft delete and is reversible via `/restore`.
|
|
> See [documentation/sklik.md](documentation/sklik.md).
|
|
|
|
Where to obtain each credential is described at the top of `/docs` (Swagger) and
|
|
in [documentation/](documentation/).
|
|
|
|
## Run locally
|
|
|
|
```bash
|
|
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](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/](documentation/) for per-integration detail.
|