Partner API
Base URL is https://adyoutiser.com/api/v1. Every call carries a bearer token. Errors come back as RFC 7807 problem documents.
Updated
Access
The API is for partners with API access. Credentials are issued by the administration. There is no self signup and no key generator in the dashboard.
- Base URL
- https://adyoutiser.com/api/v1
- Auth header
- Authorization: Bearer <token>There is no X-API-Key header.
- Error format
- application/problem+json (RFC 7807)
- Rate limit
- 600 requests per minute per access by default.
Authentication
Send the token in the Authorization header. The prefix decides how it is read: a value starting with adyo_ is treated as an API key, anything else as an OAuth access token.
curl -H "Authorization: Bearer adyo_your_api_key" \
"https://adyoutiser.com/api/v1/screens?city=Wien&limit=50"curl -X POST "https://adyoutiser.com/api/v1/oauth/token" \
-H "Content-Type: application/json" \
-d '{"client_id":"YOUR_CLIENT_ID","client_secret":"YOUR_CLIENT_SECRET"}'{
"access_token": "...",
"token_type": "bearer",
"expires_in": 3600,
"scope": "screens:read"
}The token endpoint is throttled hard
10 requests per minute per IP. Cache the token for its full hour instead of fetching one per call.
Limits and errors
| Scope | Limit |
|---|---|
| POST /oauth/token | 10 per minute per IP. |
| All other endpoints | 600 per minute per access, by default. |
HTTP/1.1 429 Too Many Requests
Content-Type: application/problem+json
Retry-After: 60Every error body follows RFC 7807 with the content type application/problem+json.
- type
- Identifier of the error type.
- title
- Short label for the error.
- status
- The HTTP status code.
- detail
- What went wrong in this specific call.
- retry_after
- Only on 429. Seconds to wait.
Endpoints
| Method | Path | Scope |
|---|---|---|
| POST | /api/v1/oauth/token | — |
| GET | /api/v1/screens | screens:read |
| GET | /api/v1/screens/{screenId} | screens:read |
| GET | /api/v1/screens/{screenId}/availability | screens:read |
| POST | /api/v1/coupons | coupons:write |
| GET | /api/v1/coupons/{code} | coupons:read | coupons:write |
| POST | /api/v1/coupons/{code}/revoke | coupons:write |
| GET | /api/v1/users/lookup | users:read |
| POST | /api/v1/landing-links | links:write |
| GET | /api/v1/campaign-requests | campaign_requests:read |
| GET | /api/v1/campaign-requests/{id} | campaign_requests:read |
Screens. GET /screens returns { data, pagination } with cursor, has_more and total_count. Filter by country (2 letters), city, orientation and bbox. limit defaults to 50 and maxes out at 200; page with cursor.
{
"id": "...",
"name": "...",
"description": "...",
"country": "AT",
"city": "Wien",
"address": "...",
"latitude": 0,
"longitude": 0,
"orientation": "portrait",
"resolution_width": 1080,
"resolution_height": 1920,
"price_per_day_cents": 0,
"ad_sov_pct": 0,
"thumbnail_url": "...",
"venue_type": "...",
"is_active": true,
"created_at": "..."
}404 does not mean gone
GET /screens/{id} answers 404 also when the screen exists but is not bookable.
Availability. from and to are required, format YYYY-MM-DD. The response carries screen_id, from, to, max_sov_pct, used_sov_pct, available_sov_pct and price_per_day_cents.
curl -H "Authorization: Bearer adyo_your_api_key" \
"https://adyoutiser.com/api/v1/screens/SCREEN_ID/availability?from=2026-09-01&to=2026-09-07"Coupons. POST /coupons answers 201. Required: discount_type (percent, amount_cents or free_days), discount_value greater than 0, and lead_email. Optional: code, max_redemptions (1–1000, default 1), valid_until, lead_metadata. Without your own code one is generated in the pattern BVCC-XXXX.
curl -X POST "https://adyoutiser.com/api/v1/coupons" \
-H "Authorization: Bearer adyo_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"discount_type": "percent",
"discount_value": 10,
"lead_email": "lead@example.com",
"max_redemptions": 1
}'| Status | When |
|---|---|
| 400 | percent above 100, or a revoke on a coupon that is no longer active or was already redeemed. |
| 409 | The code is already taken, or that lead_email already has an active coupon on your access. One active coupon per lead email per access. |
Users. GET /users/lookup takes email or vat_uid. You get either { "data": { "exists": false } } or a record with user_id, registered_at, has_bookings and optionally attribution. Attribution data comes back only when the referral belongs to your own access.
Landing links. POST /landing-links answers 201 and returns a url with UTM parameters attached. utm_medium is always partner_api.
Campaign requests. GET /campaign-requests filters on status (rejected, abandoned, no_inventory, converted), from, to and industry. You only ever see your own requests.
Webhooks
| Event | Fires when |
|---|---|
| user.registered | A referred user registers. |
| campaign.paid | A campaign is paid. |
| coupon.redeemed | One of your coupons is redeemed. |
| campaign_request.created | A campaign request comes in. |
| campaign_request.rejected | A campaign request is rejected. |
X-Adyoutiser-Signature: t=1754438400,v1=<hex>Retries
On failure delivery is retried up to 5 times, spaced 1 s, 5 s, 30 s, 2 min, 10 min. After that the delivery counts as exhausted — answer with 2xx quickly and do the work afterwards.
Related
- Partner overviewYou own the screen. We sell the ad time and pay you per campaign. What that means in practice.
- How Adyoutiser worksReal screens in Vienna and Bratislava. What you book is a share of ad time — here is how that share is split.