search a ads
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
# Google Ads
|
||||
|
||||
Proxy over the Google Ads API `GoogleAdsService` (GAQL search / searchStream),
|
||||
which covers virtually all Google Ads reporting. Bodies are forwarded as-is.
|
||||
|
||||
The API version is in an env var (`GOOGLE_ADS_API_VERSION`, default `v19`)
|
||||
because Google deprecates versions roughly yearly — bump it without a code
|
||||
change. Base URL: `https://googleads.googleapis.com/{version}`.
|
||||
|
||||
## Credentials
|
||||
|
||||
Google Ads needs more than a Bearer token:
|
||||
|
||||
| Header | Required | Meaning |
|
||||
| --- | --- | --- |
|
||||
| `X-GAds-Developer-Token` | **yes** | Developer token from a Google Ads manager account → `developer-token`. |
|
||||
| `X-GAds-Access-Token` | one of these | Ready OAuth2 access token (Bearer). |
|
||||
| `X-GAds-Credentials` | one of these | Base64 service-account JSON (scope `adwords`); needs domain-wide delegation. |
|
||||
| `X-GAds-Login-Customer-Id` | no | Manager (MCC) id → `login-customer-id`. Digits only. |
|
||||
| `X-GAds-Quota-Project` | no | GCP project id → `x-goog-user-project`. |
|
||||
|
||||
> For Google Ads a service account works only with **domain-wide delegation**;
|
||||
> in practice the simplest path is a ready OAuth2 access token (obtained from a
|
||||
> refresh token with scope `https://www.googleapis.com/auth/adwords`) in
|
||||
> `X-GAds-Access-Token`.
|
||||
|
||||
## Endpoints
|
||||
|
||||
| Method | Path | Purpose |
|
||||
| --- | --- | --- |
|
||||
| POST | `/googleads/customers/{customer_id}/search` | GAQL query, paginated. |
|
||||
| POST | `/googleads/customers/{customer_id}/searchStream` | GAQL query, whole result set in one streamed response. |
|
||||
| GET | `/googleads/customers:listAccessibleCustomers` | Customer ids the credentials can access. |
|
||||
|
||||
`customer_id` is the 10-digit account id (dashes are stripped for you).
|
||||
|
||||
### Query body (GAQL)
|
||||
|
||||
```json
|
||||
{
|
||||
"query": "SELECT campaign.id, campaign.name, metrics.impressions, metrics.clicks, metrics.cost_micros FROM campaign WHERE segments.date DURING LAST_7_DAYS"
|
||||
}
|
||||
```
|
||||
|
||||
GAQL reference:
|
||||
<https://developers.google.com/google-ads/api/docs/query/overview>.
|
||||
|
||||
## Kde získat údaje (návod pro klienta)
|
||||
|
||||
- **Developer token**: v Google Ads **manager (MCC) účtu → Tools → API Center**.
|
||||
Token musí mít schválený (approved) přístup, jinak vrací jen test účty.
|
||||
- **customer_id**: 10místné číslo účtu (vpravo nahoře v Google Ads, bez pomlček).
|
||||
- **login-customer-id**: ID manager účtu, přes který přistupujete (volitelné).
|
||||
- **Access token**: vygenerujte z refresh tokenu se scope
|
||||
`https://www.googleapis.com/auth/adwords` (např. OAuth Playground) → hlavička
|
||||
`X-GAds-Access-Token`.
|
||||
|
||||
## Errors
|
||||
|
||||
Upstream errors keep the Google Ads status and body (often a detailed
|
||||
`GoogleAdsFailure`) in `upstream_body`. A common one: developer token not
|
||||
approved, or `login-customer-id` required for manager access.
|
||||
|
||||
## curl example
|
||||
|
||||
```bash
|
||||
curl -X POST "https://services.csbot.cz/apps/analytics/googleads/customers/1234567890/searchStream" \
|
||||
-H "X-GAds-Developer-Token: <DEV_TOKEN>" \
|
||||
-H "X-GAds-Access-Token: ya29...." \
|
||||
-H "X-GAds-Login-Customer-Id: 9876543210" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query":"SELECT campaign.name, metrics.clicks FROM campaign WHERE segments.date DURING LAST_7_DAYS"}'
|
||||
```
|
||||
@@ -0,0 +1,71 @@
|
||||
# Google Search Console
|
||||
|
||||
Proxy over the Search Console API. Search Analytics, Sites and Sitemaps use the
|
||||
Webmasters v3 API (`www.googleapis.com/webmasters/v3`); URL Inspection uses
|
||||
`searchconsole.googleapis.com/v1`. Read-only.
|
||||
|
||||
## Credentials
|
||||
|
||||
Same Google OAuth model as Analytics, token wins over service account:
|
||||
|
||||
| Header | Meaning |
|
||||
| --- | --- |
|
||||
| `X-GSC-Access-Token` | Ready OAuth2 access token (Bearer). |
|
||||
| `X-GSC-Credentials` | Base64 service-account JSON; token minted with scope `https://www.googleapis.com/auth/webmasters.readonly`. |
|
||||
| `X-GSC-Quota-Project` | Optional GCP project id → `x-goog-user-project`. |
|
||||
|
||||
The service account (or token's user) must be added to the property in Search
|
||||
Console → **Settings → Users and permissions**.
|
||||
|
||||
## Site URL
|
||||
|
||||
Every endpoint takes the property as the `siteUrl` query parameter (the proxy
|
||||
URL-encodes it into the upstream path):
|
||||
|
||||
- URL-prefix property: `https://example.com/`
|
||||
- Domain property: `sc-domain:example.com`
|
||||
|
||||
## Endpoints
|
||||
|
||||
| Method | Path | Purpose |
|
||||
| --- | --- | --- |
|
||||
| POST | `/gsc/searchAnalytics/query?siteUrl=` | Search traffic (clicks, impressions, CTR, position). |
|
||||
| GET | `/gsc/sites` | List sites in the account. |
|
||||
| GET | `/gsc/site?siteUrl=` | Single site info + permission level. |
|
||||
| GET | `/gsc/sitemaps?siteUrl=` | List submitted sitemaps. |
|
||||
| GET | `/gsc/sitemap?siteUrl=&feedpath=` | One sitemap's details. |
|
||||
| POST | `/gsc/urlInspection` | Index status of a URL (body has `inspectionUrl`, `siteUrl`, `languageCode`). |
|
||||
|
||||
### Search Analytics query body
|
||||
|
||||
```json
|
||||
{
|
||||
"startDate": "2026-05-01",
|
||||
"endDate": "2026-05-31",
|
||||
"dimensions": ["query", "page"],
|
||||
"rowLimit": 100
|
||||
}
|
||||
```
|
||||
|
||||
Forwarded unchanged; see
|
||||
<https://developers.google.com/webmaster-tools/v1/searchanalytics/query>.
|
||||
|
||||
## Kde získat údaje (návod pro klienta)
|
||||
|
||||
1. Service account a base64 JSON klíč – viz hlavní popis ve Swaggeru / [google-analytics.md](google-analytics.md).
|
||||
2. V [Search Console](https://search.google.com/search-console) → **Nastavení →
|
||||
Uživatelé a oprávnění** přidejte `client_email` service accountu.
|
||||
3. `siteUrl` = adresa property tak, jak je uvedená v Search Console.
|
||||
|
||||
## Not wired (deliberately)
|
||||
|
||||
Writes — submitting/deleting sitemaps, adding/removing sites. They need the
|
||||
`webmasters` (read-write) scope; add them if management is required.
|
||||
|
||||
## curl example
|
||||
|
||||
```bash
|
||||
curl -X POST "https://services.csbot.cz/apps/analytics/gsc/searchAnalytics/query?siteUrl=https%3A%2F%2Fexample.com%2F" \
|
||||
-H "X-GSC-Access-Token: ya29...." -H "Content-Type: application/json" \
|
||||
-d '{"startDate":"2026-05-01","endDate":"2026-05-31","dimensions":["query"]}'
|
||||
```
|
||||
+24
-11
@@ -1,10 +1,17 @@
|
||||
# analytics — overview
|
||||
|
||||
A stateless multi-tenant API proxy exposing two upstream services under one
|
||||
A stateless multi-tenant API proxy exposing four upstream services under one
|
||||
FastAPI app:
|
||||
|
||||
1. **Google Analytics 4** — Data API (reporting) + Admin API (read).
|
||||
2. **Sklik** (Seznam) — Drak JSON-RPC API.
|
||||
2. **Google Search Console** — Search Analytics, Sites, Sitemaps, URL Inspection (read).
|
||||
3. **Google Ads** — GAQL reporting (search / searchStream).
|
||||
4. **Sklik** (Seznam) — Drak JSON-RPC API.
|
||||
|
||||
The three Google services share one OAuth mechanism (token wins over service
|
||||
account) — they differ only in the OAuth *scope* and the header prefix
|
||||
(`X-GA-*`, `X-GSC-*`, `X-GAds-*`). Google Ads additionally needs a developer
|
||||
token.
|
||||
|
||||
The structure mirrors the sibling `idoklad` / `csob` services (config→env,
|
||||
credentials→headers, client per upstream, routers, central exception handling,
|
||||
@@ -25,17 +32,19 @@ Swagger at `/docs`), adapted to Python/FastAPI.
|
||||
|
||||
```
|
||||
app/
|
||||
config.py env-driven config (base URLs, scope, timeout) — no secrets
|
||||
config.py env-driven config (base URLs, scopes, timeout) — no secrets
|
||||
logging_config.py get_logger(); secrets are never logged
|
||||
errors.py MissingCredentialsError, UpstreamError + handlers
|
||||
credentials.py X- header dependencies (GA + Sklik)
|
||||
credentials.py X- header dependencies (GA / GSC / Ads / Sklik)
|
||||
clients/
|
||||
ga_client.py GA Data/Admin HTTP client + service-account token minting
|
||||
google.py shared Google client: Bearer/SA token minting + requests
|
||||
sklik_client.py Sklik JSON-RPC client (login + session + report paging)
|
||||
routers/
|
||||
meta.py /health, /version
|
||||
ga_data.py /ga/data/...
|
||||
ga_admin.py /ga/admin/...
|
||||
ga_data.py /ga/data/... (Google Analytics Data)
|
||||
ga_admin.py /ga/admin/... (Google Analytics Admin)
|
||||
gsc.py /gsc/... (Search Console)
|
||||
googleads.py /googleads/... (Google Ads)
|
||||
sklik.py /sklik/...
|
||||
main.py app factory, root_path, router + handler registration
|
||||
```
|
||||
@@ -50,14 +59,18 @@ routes are unprefixed (Caddy `handle_path` strips the prefix).
|
||||
|
||||
| Upstream | Header(s) | Behaviour |
|
||||
| --- | --- | --- |
|
||||
| Google Analytics | `X-GA-Access-Token` **or** `X-GA-Credentials` (+ `X-GA-Quota-Project`) | Token used directly; else a token is minted from the base64 service-account JSON (scope `analytics.readonly`) and cached in memory until ~60 s before expiry. |
|
||||
| Google Analytics | `X-GA-Access-Token` **or** `X-GA-Credentials` (+ `X-GA-Quota-Project`) | Token used directly; else minted from base64 service-account JSON (scope `analytics.readonly`) and cached in memory until ~60 s before expiry. |
|
||||
| Search Console | `X-GSC-Access-Token` **or** `X-GSC-Credentials` (+ `X-GSC-Quota-Project`) | Same as GA, scope `webmasters.readonly`. |
|
||||
| Google Ads | `X-GAds-Developer-Token` (req) + `X-GAds-Access-Token` **or** `X-GAds-Credentials` (+ `X-GAds-Login-Customer-Id`, `X-GAds-Quota-Project`) | Same OAuth (scope `adwords`) plus `developer-token` / `login-customer-id` headers forwarded upstream. |
|
||||
| Sklik | `X-Sklik-Token` (+ `X-Sklik-User-Id`) | `client.loginByToken` per request → session injected into the call. |
|
||||
|
||||
## Deliberately not wired
|
||||
|
||||
- **GA Admin write operations** (create/update/delete properties, streams). The
|
||||
requested scope is read-only (`analytics.readonly`); add `analytics.edit` and
|
||||
endpoints if management is needed later.
|
||||
- **Write operations** across the Google services: GA Admin (create/update
|
||||
properties, streams), Search Console (submit/delete sitemaps, add/remove
|
||||
sites), Google Ads mutates (create/update campaigns etc.). All requested
|
||||
scopes are read-only; add the read-write scope + endpoints if management is
|
||||
needed later. Google Ads exposes reporting (GAQL) only for now.
|
||||
- **Sklik header-credential encryption.** Same deferral as `idoklad`/`csob`:
|
||||
header values are plaintext over TLS for now.
|
||||
- **Sklik session reuse across requests** — the chosen model logs in per
|
||||
|
||||
Reference in New Issue
Block a user