Reseller API Documentation

Summary

Reseller API is an API, where the Resellers can list, search and buy videos from Glymt.

The Reseller API lets your platform browse and search Glymt's catalogue, read every video's metadata, show its cover and its unbranded previews in two resolutions, and license a video for 20 credits to download the unwatermarked file. API version 1.0; https only — http is refused.

Contact us

API clients are issued by Glymt on request. Glymt reviews the request and issues a client ID and secret.

Base URL

Every request of the Reseller API goes to this base URL. Only https is accepted: a request over http is refused.

Every answer is JSON with its own HTTP status. An unknown path under the base answers 404 with {"error": "not_found", "documentation": …}.

Base URL
https://api.glymt.com/reseller/v1

Authentication

The Reseller API uses OAuth 2.0 client credentials (RFC 6749 §4.4). Glymt issues your platform a client ID and a client secret. The secret is shown once, when it is issued: keep it on your server, never in a browser or an app. If it is lost or exposed, ask us for a new one — the old one stops working at once and every token issued with it is revoked. Glymt can also revoke a token or a client on request.

Request a token from POST /oauth/token with grant_type=client_credentials, authenticating the client with HTTP Basic (client_id:client_secret) — or with client_id and client_secret in the form body, never both. A user's username or password is never accepted.

Send the token on every other call in the header Authorization: Bearer {access_token}. A token lasts 7200 seconds (expires_in); when it expires, request a new one the same way. A token of the Reseller API is refused by the Submitter API, and a Submitter API token is refused here (401 invalid_token).

Errors of the token request (RFC 6749 §5.2): 400 invalid_request (no grant_type, user credentials sent, the client sent twice), 400 unsupported_grant_type, 401 invalid_client (unknown client, wrong secret, a client or owner that is not active), with WWW-Authenticate. Errors of every other call: 401 invalid_token (no token, unknown, expired, revoked, or issued for the other API) and 403 insufficient_scope, with WWW-Authenticate: Bearer.

POST /oauth/token — HTTP Basic
curl -s -X POST https://api.glymt.com/reseller/v1/oauth/token \
  -u '{client_id}:{client_secret}' \
  -d grant_type=client_credentials
POST /oauth/token — the form body
curl -s -X POST https://api.glymt.com/reseller/v1/oauth/token \
  -d grant_type=client_credentials \
  -d client_id={client_id} \
  --data-urlencode client_secret={client_secret}
Response 200
{
  "access_token": "{access_token}",
  "token_type": "Bearer",
  "expires_in": 7200,
  "scope": "reseller"
}
Response 401
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm="Glymt Reseller API"

{ "error": "invalid_client", "error_description": "Client authentication failed." }
Every other call
curl -s https://api.glymt.com/reseller/v1/account/summary \
  -H 'Authorization: Bearer {access_token}'

Endpoints

Every endpoint has its own path. Base URL https://api.glymt.com/reseller/v1.

MethodPathEndpoint
GET / welcome (name, version, documentation URL; no token)
POST /oauth/token token (OAuth 2.0 client credentials; no Bearer)
GET /videos/browse browse the client's catalogue
GET /videos/search full-text search (OpenSearch)
GET /videos/details/:video_id all metadata of one video
POST /ai_search/start start an AI search (text and/or up to 2 images)
GET /ai_search/results/:search_id poll / read an AI search
POST /licenses/purchase license a video (credits debited)
GET /licenses/list this client's licences
GET /licenses/download_link/:licence_id 120 s download link of a held licence
GET /licenses/model_releases/:licence_id the licensed video's model releases
GET /account/summary credits and statistics, read only

Objects

The catalogue lists videos whose unbranded previews are ready; it grows as previews are generated. Your catalogue is the published videos whose creators allow your client's type (stock, training or other). Every URL is absolute (https on glymt.com); an asset that does not exist yet is null, never a broken URL. Watermarked previews and the original file are never in an object: the unwatermarked file is reached only through a licence's download link.

VideoCard
FieldTypeMeaning
idintegerthe video's id (the video_id of every other endpoint)
tokenstringthe video's public token
titlestringits title
durationnumberseconds
fpsintegerframes per second
resolution_width, resolution_heightintegerthe original's pixels
orientationstringlandscape | portrait | square
has_audiobooleanthe video has a sound track
is_aibooleanthe video is AI-generated
price_creditsintegeralways 20 — every licence costs 20 credits
licensedbooleanyour account already holds a licence for it
assets.cover.small, .medium, .largeURL or nullthe cover image in three sizes
assets.preview_sdURL or nullthe unbranded preview, 570×320
assets.preview_hdURL or nullthe unbranded preview, 1920×1080
VideoCard
{
  "id": {video_id},
  "token": "{video_token}",
  "title": "{video_title}",
  "duration": 12.0,
  "fps": 25,
  "resolution_width": 1920,
  "resolution_height": 1080,
  "orientation": "landscape",
  "has_audio": true,
  "is_ai": false,
  "price_credits": 20,
  "licensed": false,
  "assets": {
    "cover": {
      "small": "https://glymt.com/{cover_small_path}",
      "medium": "https://glymt.com/{cover_medium_path}",
      "large": "https://glymt.com/{cover_large_path}"
    },
    "preview_sd": "https://glymt.com/uploads/video/{video_id}/unbranded_sd.mp4",
    "preview_hd": "https://glymt.com/uploads/video/{video_id}/unbranded_hd.mp4"
  }
}
VideoDetail

Every VideoCard field, and:

FieldTypeMeaning
descriptionstringits description
keywordsarray of stringsthe published keywords
categoriesarray of stringsthe category names
locationarray of strings or nullplace names (city, country) when known — never coordinates
model_release_countintegerthe model releases attached to the video
created_at, published_atISO 8601 UTCwhen it was uploaded and published
licence_idinteger or nullyour licence for it, when you hold one
Licence
FieldTypeMeaning
idintegerthe licence id (licence_id)
video_idintegerthe licensed video
price_creditsintegerthe credits it debited (20)
created_atISO 8601 UTCwhen it was purchased

Paging, sorting and filters

/videos/browse, /videos/search and /ai_search/results/:search_id take the same query parameters. An unknown parameter or a malformed value is 400 invalid_request — never ignored.

ParameterValues
pagewhole number from 1 (default 1)
per_page1 – 100 (default 50); page × per_page reaches at most 10,000 results on search
sortnewest | popular | rating | relevance (browse: default newest; search: default relevance; AI-search results are always in relevance order and take no sort)
resolution720 | 1080 | 2160 (the original's height)
orientationlandscape | portrait | square
fpswhole number
duration_min, duration_maxseconds (up to 3 decimals)
has_audio, model_releases, geolocationtrue | false
categoriescategory names, comma-separated (up to 50)
only_ai, exclude_aitrue | false (not both true)
created_after, created_beforeYYYY-MM-DD

GET/

The API's name, version and documentation. No token. No parameters.

Request
curl -s https://api.glymt.com/reseller/v1
Response 200
{
  "name": "Glymt Reseller API",
  "version": "1.0",
  "documentation": "https://glymt.com/reseller_api_documentation",
  "description": "{summary}"
}

POST/oauth/token

A Bearer token for 7200 seconds — see Authentication. Form body: grant_type (client_credentials), and the client by HTTP Basic or client_id + client_secret.

Errors: 400 invalid_request, 400 unsupported_grant_type, 401 invalid_client, 429 (too many token requests).

Request
curl -s -X POST https://api.glymt.com/reseller/v1/oauth/token \
  -u '{client_id}:{client_secret}' \
  -d grant_type=client_credentials
Response 200
{
  "access_token": "{access_token}",
  "token_type": "Bearer",
  "expires_in": 7200,
  "scope": "reseller"
}

GET/videos/browse

Your catalogue, a page at a time: page, per_page, sort and the filters. sort=relevance ranks the catalogue through the search index without a query. Returns {count, page, per_page, videos: [VideoCard]} — count is the number of catalogue videos matching the filters.

Errors: 400 invalid_request, 401 invalid_token, 429.

Request
curl -s 'https://api.glymt.com/reseller/v1/videos/browse?page=1&per_page=50&sort=newest&resolution=1080&has_audio=true' \
  -H 'Authorization: Bearer {access_token}'
Response 200
{
  "count": {matching_videos},
  "page": 1,
  "per_page": 50,
  "videos": [ {VideoCard}, … ]
}

GET/videos/details/:video_id

All the metadata of one catalogue video: {video: VideoDetail}. No query parameters.

Errors: 400 invalid_request, 401 invalid_token, 404 not_found (a video outside your catalogue).

Request
curl -s https://api.glymt.com/reseller/v1/videos/details/{video_id} \
  -H 'Authorization: Bearer {access_token}'
Response 200
{
  "video": {
    … every VideoCard field …,
    "description": "{video_description}",
    "keywords": ["{keyword}", "{keyword}"],
    "categories": ["{category}"],
    "location": ["{city}", "{country}"],
    "model_release_count": 1,
    "created_at": "2026-10-01T09:30:00Z",
    "published_at": "2026-10-02T10:00:00Z",
    "licence_id": null
  }
}

POST/ai_search/start

Starts an AI search by text, by up to 2 reference images, or both. JSON body: q (text, at most 750 characters), images (each {"data": base64, "content_type": "image/jpeg" | "image/png" | …}, a data: URL string, or {"url": "https://…"} on a public host; each at most 2 MB), auto_filter (true | false). The search runs in the background: the answer is 202 with the search_id to poll.

Limits per account: 60 AI searches an hour, 3 in flight, 6 seconds between two starts — 429 with Retry-After.

Errors: 400 invalid_request (an unknown parameter), 401 invalid_token, 422 invalid_request (no q and no images, q too long, too many images), 422 invalid_image (unreadable, too large, not an image, a URL that is not public), 429 too_many_requests.

Request
curl -s -X POST https://api.glymt.com/reseller/v1/ai_search/start \
  -H 'Authorization: Bearer {access_token}' \
  -H 'Content-Type: application/json' \
  -d '{
        "q": "{search_text}",
        "auto_filter": false,
        "images": [
          { "data": "{base64_image}", "content_type": "image/jpeg" },
          { "url": "https://{your_host}/{image}.jpg" }
        ]
      }'
Response 202
HTTP/1.1 202 Accepted

{
  "search_id": {search_id},
  "state": "processing",
  "auto_filter": false,
  "images_accepted": 2,
  "results": "/reseller/v1/ai_search/results/{search_id}"
}

GET/ai_search/results/:search_id

Poll an AI search: state is processing, failed (with error) or completed — then the results of your catalogue in relevance order, with page, per_page and the filters (no sort). Poll every few seconds.

Errors: 400 invalid_request, 401 invalid_token, 404 not_found (an unknown search or another account's).

Request
curl -s 'https://api.glymt.com/reseller/v1/ai_search/results/{search_id}?page=1&per_page=50' \
  -H 'Authorization: Bearer {access_token}'
Responses 200
{ "search_id": {search_id}, "state": "processing" }

{ "search_id": {search_id}, "state": "failed", "error": "{reason}" }

{
  "search_id": {search_id},
  "state": "completed",
  "count": {matching_videos},
  "page": 1,
  "per_page": 50,
  "videos": [ {VideoCard}, … ]
}

POST/licenses/purchase

Licenses one catalogue video for 20 credits, debited from your account's credits in the same step: body {"video_id": …} and nothing else (a price, a credit amount or any other parameter is 400 — the price is Glymt's). 201 with the licence and your credits left. A video your account already holds is 200 with that licence and "held": true — nothing is debited twice, so a repeated request is safe.

Errors: 400 invalid_request, 401 invalid_token, 402 insufficient_credits (with your credits and the price; nothing is written), 403 insufficient_scope (a test client licenses test videos only), 404 not_found (outside your catalogue), 409 own_video.

Request
curl -s -X POST https://api.glymt.com/reseller/v1/licenses/purchase \
  -H 'Authorization: Bearer {access_token}' \
  -H 'Content-Type: application/json' \
  -d '{ "video_id": {video_id} }'
Response 201
HTTP/1.1 201 Created

{
  "licence": { "id": {licence_id}, "video_id": {video_id}, "price_credits": 20, "created_at": "2026-10-30T12:00:00Z" },
  "credits_left": 80
}
Response 200 (already held)
HTTP/1.1 200 OK

{
  "licence": { "id": {licence_id}, "video_id": {video_id}, "price_credits": 20, "created_at": "2026-10-30T12:00:00Z" },
  "credits_left": 80,
  "held": true
}
Response 402
HTTP/1.1 402 Payment Required

{ "error": "insufficient_credits", "credits": 19, "price": 20 }

GET/licenses/list

Your account's licences, newest first: page, per_page (1 – 100, default 50).

Errors: 400 invalid_request, 401 invalid_token.

Request
curl -s 'https://api.glymt.com/reseller/v1/licenses/list?page=1&per_page=50' \
  -H 'Authorization: Bearer {access_token}'
Response 200
{
  "count": {licences},
  "page": 1,
  "per_page": 50,
  "licences": [
    { "id": {licence_id}, "video_id": {video_id}, "price_credits": 20, "created_at": "2026-10-30T12:00:00Z" }
  ]
}

GET/licenses/model_releases/:licence_id

The model releases of a licensed video, each with a signed link that lasts 120 seconds.

Errors: 400 invalid_request, 401 invalid_token, 404 not_found.

Request
curl -s https://api.glymt.com/reseller/v1/licenses/model_releases/{licence_id} \
  -H 'Authorization: Bearer {access_token}'
Response 200
{
  "licence_id": {licence_id},
  "count": 1,
  "model_releases": [
    { "name": "{release_name}", "url": "{signed_release_url}", "expires_in": 120 }
  ]
}

GET/account/summary

Read only: your client, the licence price, your credits available and spent, your licences, your searches against their limits and your token's issue and expiry. No parameters.

Errors: 400 invalid_request, 401 invalid_token.

Request
curl -s https://api.glymt.com/reseller/v1/account/summary \
  -H 'Authorization: Bearer {access_token}'
Response 200
{
  "client": { "name": "{client_name}", "type": "training", "client_id": "{client_id}" },
  "price_credits": 20,
  "credits_available": 80,
  "credits_spent": 20,
  "licences_count": 1,
  "licences_via_api": 1,
  "last_licence_at": "2026-10-30T12:00:00Z",
  "licences_per_month": { "2026-10": 1 },
  "searches": { "this_hour": 12, "today": 40, "limit_per_minute": 600 },
  "ai_searches": { "this_hour": 2, "today": 5, "limit_per_hour": 60 },
  "token": { "issued_at": "2026-10-30T11:00:00Z", "expires_at": "2026-10-30T13:00:00Z" }
}

Licensing and credits

Every licence costs 20 credits; Glymt adds credits to your account; nothing in the API buys or adds credits.

  • One fixed price: 20 credits a licence, for every video of the catalogue. The price is never read from a request.
  • Credits are added to your account by Glymt — contact us. No endpoint and no page of the API buys, adds, moves or converts credits; /account/summary shows them.
  • A purchase checks and debits your credits in one step: with fewer than 20 the answer is 402 insufficient_credits and nothing is written.
  • Purchasing a video your account already holds answers that licence and debits nothing.
  • The download link of a held licence lasts 120 seconds and can be requested again at any time; the model releases of the licensed video come from /licenses/model_releases/:licence_id.

Errors

Every error is JSON with its HTTP status — {"error": code, "message": text}, or the OAuth form {"error": code, "error_description": text} for 400/401/403 of the token and the Bearer checks.

Error body
{ "error": "invalid_request", "message": "unknown parameter(s): price" }
StatuserrorWhen
400invalid_request, unsupported_grant_typea missing, unknown or malformed parameter; a malformed JSON body
401invalid_clientthe token request: unknown client, wrong secret, inactive client or owner
401invalid_tokenno token, or one unknown, expired, revoked or issued for the Submitter API
402insufficient_creditsa purchase with fewer credits than the price
403insufficient_scopea valid token asking for something its client may not do
404not_foundan unknown path, or a video, search or licence outside your account
409own_video, download_unavailablelicensing your own video; the licensed file is not available right now
413—a request body above the size limit, refused before it reaches the API
415—not answered by v1: a body that is neither JSON nor form-encoded is not read, and the request answers 400
422invalid_request, invalid_imagean AI search without text or images, or with an image that cannot be used
423—not answered by the Reseller API
429too_many_requestsa limit reached — wait the Retry-After seconds
500server_erroran error on Glymt's side; retry later
404
{ "error": "not_found", "documentation": "https://glymt.com/reseller_api_documentation" }

Limits

  • Token requests: 10 a minute and 100 an hour per client, 20 a minute per IP address.
  • Every other call: 600 a minute per token.
  • AI search: 60 an hour, 3 in flight, 6 seconds between two starts, up to 2 images of at most 2 MB.
  • Above a limit the answer is 429 with Retry-After (seconds).

Changelog

API version 1.0.

  • 1.0 — OAuth 2.0 client credentials (client ID and secret; Bearer tokens of 7200 seconds, one per API); every endpoint at its own path (the table above); absolute asset URLs with the unbranded previews in two resolutions; licences at 20 credits debited in one step. The earlier login with a username and password, the /videos… endpoints and the account balance endpoint are removed.
admin login browse the video dataset site administration php login page configuration file