Growwer

Growwer API

The link building API

Connect Growwer to your own systems. Search the media catalogue for your projects, read prices and flash offers, place and follow orders or, as a seller, handle the orders you receive. Read the documentation.

A Growwer account is required. You create your API key on your profile.

A request to the Growwer API for flash offers, next to a store, Make, n8n and a BI dashboard connected to it

Growwer, inside your own tools

Your account, over HTTP
68 REST operations over your account, projects, the media catalogue, orders from estimate to delivery, the managed service and the media you sell. A key never does more than your account can do on the web.
Estimate first, retry with the same key
Estimate an order against your balance before you place it. Every write takes an Idempotency-Key, so a retry with the same key never places a second order, and a key can only spend the balance already in your wallet.
A public contract and seven SDKs
The OpenAPI contract is public, and the reference on this page is rendered from it. Official clients for TypeScript, Python, PHP, Go, Java, .NET and Ruby are generated from the same contract, and there is a Postman collection.

What the API does for you

A few of the operations, by who uses them. The reference below documents all 68.

Buyers

  • Search the catalogue for a project with the web's filters and read its flash offers.

    GET /projects/{id}/medias

  • Ask for specific media you do not find for a project.

    POST /projects/{id}/media-requests

  • Estimate an order against your balance, then place it.

    POST /orders/estimate

  • Review drafts, approve deliveries or request changes.

    POST /orders/{id}/approve

Agencies

  • Create and edit each client's project from your own systems.

    POST /projects

  • Follow every order by asking for what changed since your last call.

    GET /orders

  • With a managed service, request campaigns and accept or reject their proposals.

    POST /managed-service/campaigns/{id}/proposals/review

  • Read wallet movements and download invoices for your reports.

    GET /invoices

Sellers

  • Register media one at a time and edit their fees and offer.

    POST /seller/medias

  • Accept, reject and deliver the orders you receive.

    POST /seller/orders/{id}/deliver

  • Deliver the updates a buyer requests, or refuse them.

    POST /seller/orders/{id}/deliver-update

  • Set your holiday and read your wallet and withdrawals.

    PUT /seller/holiday

Growwer API

Most of what you do in Growwer as a buyer or a seller is reachable over HTTP: your account, projects and saved filters, the media catalogue, orders from estimate to delivery, the managed service, and on the seller side your medias and the orders they receive. Wallets, invoices and withdrawals are read-only: money movements such as top-ups and withdrawal requests stay on the web. 68 operations, authenticated with an API key you create yourself.

The OpenAPI contract is the source of truth, and it is public: reading it needs no key. Every operation below is rendered from that contract, so this page cannot describe an API that no longer exists.

The API is one of the Growwer integrations. Prefer to work from an AI assistant? See the Growwer MCP.

Download: OpenAPI Swagger Postman

Ask AI: ChatGPT Claude

Client libraries

Official clients for seven languages, each generated from the same contract: github.com/viralitymedia/growwer-api-sdk.

Language Registry Install
TypeScript npm npm install @growwer/api
Python PyPI pip install growwer
PHP Packagist composer require growwer/growwer-api-sdk
Go Go modules go get github.com/viralitymedia/growwer-api-sdk/go
Java Maven com.growwer:growwer-api
.NET NuGet dotnet add package Growwer.Api
Ruby RubyGems gem install growwer

The snippets on this page call the API with a plain HTTP client rather than the SDKs. Most need only the language's standard library; the Python ones use requests, the Go writes use github.com/google/uuid and PHP needs its curl extension. The SDKs are the shortcut once a project is past its first call.

Getting a key

Keys are created from . A key starts with gw_sk_ and can carry an expiry date; you can have several and revoke any of them.

The full key is shown once, when you create it, and never again. Store it where you store any other secret. If you lose it, revoke it and create another.

Each key has one capability, chosen at creation and fixed for its life: read calls every GET operation, and read_write also calls the writes. A read key that calls a write gets 403 insufficient_capability. A key never does more than your account can do on the web.

Your first request

Send the key as Authorization: Bearer gw_sk_.... GET /api/v1/me returns your account: profile basics, your buyer and seller balances, your catalogue quota for the day and whether you have a managed service. It is the quickest way to confirm a key works.

curl -X GET "https://tool.growwer.com/api/v1/me" \
  -H "Authorization: Bearer $GROWWER_API_KEY"

Example response

{
  "id": "00000000-0000-0000-0000-000000000000",
  "email": "[email protected]",
  "firstName": "string",
  "language": "en",
  "createdAt": "2026-01-01T00:00:00+00:00",
  "buyer": {
    "balance": {
      "available": {
        "amount": 12500,
        "currency": "EUR"
      },
      "pending": {
        "amount": 12500,
        "currency": "EUR"
      }
    }
  },
  "seller": {
    "balance": {
      "available": {
        "amount": 12500,
        "currency": "EUR"
      },
      "pending": {
        "amount": 12500,
        "currency": "EUR"
      }
    },
    "restricted": false,
    "onHoliday": false,
    "holidayEndDate": "2026-01-01"
  },
  "catalogue": {
    "eligible": false,
    "dailyCap": 0,
    "remainingToday": 0
  },
  "managedService": {
    "active": false
  }
}

Errors

Every error answers the same shape, {code, message, requestId, details?}. Branch on code, which is stable; message is for people and may change. requestId matches the X-Request-Id header every response carries: quote it when you report a problem.

{
  "code": "validation_failed",
  "message": "The request is not valid. See details for each field.",
  "requestId": "9b2f6c1e-4d7a-4e3b-8a5f-2c1d0e9f7a64",
  "details": {
    "fields": {
      "promotedProducts": [
        "This collection should contain 5 elements or more."
      ]
    }
  }
}

An invalid request answers 422 validation_failed, with the messages for each invalid field under details.fields, by field name.

  • 401 unauthorized: the key is missing, malformed, unknown, revoked or expired, or its account cannot sign in.
  • 403 insufficient_capability: a read key called a write.
  • 404 not_found: the resource does not exist, or it is not yours.
  • 409 invalid_state: the resource is yours but not in a state that allows the operation.
  • 500 internal_error: an unexpected error.

Each operation in the reference lists the statuses it can answer and the codes behind them.

Rate limits

600 requests per minute per user, and 300 per key. Every authenticated response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (seconds until the window resets) for whichever of the two limits is tighter.

Over the limit answers 429 rate_limit_exceeded with a Retry-After header in seconds; wait that long rather than retrying at once.

Failed authentications are limited too. After 10 failures with one key from one address within 15 minutes, that key answers 429 too_many_failed_authentications from that address. Past 300 failures from one address within 15 minutes, with any key, every failed attempt from it answers the same. Both come with Retry-After. A valid key always passes, so someone guessing keys from your network never locks out a key that works.

If the limiter itself cannot be reached, the call is refused with 503 rate_limit_unavailable; retry later. The catalogue and the managed service requests also have daily caps of their own, described next.

Catalogue access and daily caps

The catalogue operations, searching a project's medias, flash offers, media details and counting the medias of a saved filter, share a cap counted in units per UTC day. Each new distinct media they show you costs one unit. A page that holds no new media, an empty one included, costs one unit, and so does the first count of a saved filter that day. Accounts with a recent completed order, as buyer or as seller, get 5,000 units a day; any other account gets a trial quota of 100 a day, which rises to the full cap once it has a recent completed order. Eligibility is cached for a few minutes, so the full cap can take a short while to apply after your first order completes. Medias you have already seen that day are free, and so is a request already answered that day while the project's media lists, topics and language stay as they were. A media you have ordered is always readable with GET /api/v1/medias/{id} without counting at all.

GET /api/v1/me tells you where you stand: catalogue.eligible is true when you have the full cap, catalogue.dailyCap is the cap that applies to you, and catalogue.remainingToday is how many units still fit today. It is null when the count cannot be read right now, and the catalogue operations then answer 503 until it can.

A response that would take you past your cap answers 429 catalogue_daily_cap_reached, with details.remaining (how many units still fit today), details.limit (the cap that applies), details.tier (full, or trial for an account without a recent completed order) and Retry-After (the time to the next UTC midnight). Medias you have already seen that day stay readable.

The trial quota can be switched off. An account without a recent completed order then gets 403 catalogue_requires_recent_order from the catalogue operations instead, and GET /api/v1/me reports a catalogue.dailyCap of 0.

The two managed service requests, POST /api/v1/managed-service/campaign-requests and POST /api/v1/managed-service/info-requests, take 5 requests each per UTC day. Past them they answer 429 managed_service_request_limit_reached, with Retry-After the time to the next UTC midnight. A refused request (4xx) does not count; one that fails on the server (5xx) does, as it may have been sent.

Registering a media, POST /api/v1/seller/medias, is not capped, but refusing one is: a media already registered, by you or by anyone else, answers 409 media_already_registered, and each such refusal counts toward a daily limit per user. Past it, sending a media already registered answers 429 media_duplicate_limit_reached, with Retry-After the time to the next UTC midnight. Medias not registered yet are never refused by it.

Writes are idempotent

Every write requires an Idempotency-Key header: 1 to 255 printable ASCII characters without spaces. A UUID is the obvious choice. Use a new key for each operation, and reuse it only to retry that same operation. Keys belong to your account, not to one API key.

  • A successful write is stored for 30 days. A retry with the same key and the same request replays its response with Idempotent-Replayed: true and does nothing again, so a lost response never becomes a second order.
  • A refused write (4xx) has no effect and releases its key: fix the cause and retry with the same key.
  • A failed write (5xx) may have taken effect, so it never runs again under its key: a retry gets 409 idempotency_outcome_unknown. Check whether it took effect before retrying with a new key.
  • A retry while the first request is still running gets 409 idempotency_request_in_progress. A request still running after 5 minutes is treated as failed.
  • The same key with a different request (method, path, query or body) gets 409 idempotency_key_reused. A write without the header gets 400 idempotency_key_required, and a malformed one 400 invalid_idempotency_key.

The two estimates, POST /api/v1/orders/estimate and POST /api/v1/managed-service/campaigns/{id}/proposals/estimate, change nothing: they take no Idempotency-Key and a read key may call them.

Orders have one more guard. Placing the same order (same project, media, type and price) again within 5 seconds of one still in progress answers 409 duplicate_order, with details.orderId the earlier order, or null when it has finished or was deleted since. The guard covers only those 5 seconds: a retry after them with a new Idempotency-Key places and pays a second order. To retry an order safely, reuse its key.

There are no batch writes. To register many medias, call POST /api/v1/seller/medias once per media, each with its own Idempotency-Key, and read each answer on its own: one media's 409 or 422 does not stop the others. The rate limits apply to the loop as to any other calls.

Deleting a media, DELETE /api/v1/seller/medias/{id}, never cancels an order. While any order on the media is not finished it answers 409 media_has_active_orders, with those orders in details.orderIds, and deletes nothing.

Paging, money and dates

Listings answer {items, total} and take limit (default 20, from 1 to 100) and offset (default 0). A limit above 100 is refused with 422 validation_failed, never quietly clamped, so the page you asked for is the page you get. total counts everything the filters match, not the size of the page.

Money is an integer amount in cents plus a currency code: {"amount": 12500, "currency": "EUR"} is 125.00 EUR. Dates are ISO-8601; in a query string, send the + of an offset as %2B.

There are no webhooks. To follow your orders, poll GET /api/v1/orders with sort=updatedAt and updatedSince set to the last updatedAt you saw. Responses may gain fields over time, so ignore the ones you do not know.

Uploads and downloads

A write that takes a document, such as the draft you propose with an order or the one a seller delivers, references a file you upload first. POST /api/v1/files takes a multipart/form-data upload of an odt, doc, docx or txt file of up to 8 MB and returns its id. A file can be used by one write and expires 24 hours after the upload.

You may hold 20 pending uploads: files that no write has used yet and that have not expired. Past them, POST /api/v1/files answers 429 file_limit_reached, with details.limit (how many you may hold) and Retry-After (the seconds until the first of them expires). Use one of your pending files in a write, which frees its place at once, or wait that long. An upload refused as invalid (422) does not count.

Downloads, such as GET /api/v1/invoices/{id}/pdf, answer the file itself with its own content type, not JSON. Their errors still use the JSON shape above.

General

Account

Your profile, balances and catalogue quota, and the codes and names the API uses.

GET /api/v1/me

Your account

Profile basics, the buyer and seller balances, whether your seller account is restricted, whether the catalogue is open to you, and whether you have a managed service.

Responses

  • 200

    Your account.

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X GET "https://tool.growwer.com/api/v1/me" \
  -H "Authorization: Bearer $GROWWER_API_KEY"

Example response

{
  "id": "00000000-0000-0000-0000-000000000000",
  "email": "[email protected]",
  "firstName": "string",
  "language": "en",
  "createdAt": "2026-01-01T00:00:00+00:00",
  "buyer": {
    "balance": {
      "available": {
        "amount": 12500,
        "currency": "EUR"
      },
      "pending": {
        "amount": 12500,
        "currency": "EUR"
      }
    }
  },
  "seller": {
    "balance": {
      "available": {
        "amount": 12500,
        "currency": "EUR"
      },
      "pending": {
        "amount": 12500,
        "currency": "EUR"
      }
    },
    "restricted": false,
    "onHoliday": false,
    "holidayEndDate": "2026-01-01"
  },
  "catalogue": {
    "eligible": false,
    "dailyCap": 0,
    "remainingToday": 0
  },
  "managedService": {
    "active": false
  }
}

GET /api/v1/vocabulary

Codes and names

The countries, languages, topics, product types, link types and order states that other operations take and return. Names are in English.

Responses

  • 200

    Every code, with its English name where it has one.

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X GET "https://tool.growwer.com/api/v1/vocabulary" \
  -H "Authorization: Bearer $GROWWER_API_KEY"

Example response

{
  "countries": [
    {
      "code": "string",
      "name": "string"
    }
  ],
  "languages": [
    {
      "code": "string",
      "name": "string"
    }
  ],
  "topics": [
    {
      "id": 0,
      "name": "string"
    }
  ],
  "productTypes": [
    "post_order"
  ],
  "linkTypes": [
    "dofollow"
  ],
  "orderStates": [
    "string"
  ]
}

Files

Upload the documents that a later write, such as an order's briefing, references.

POST /api/v1/files

Upload a file

Uploads a document (odt, doc, docx or txt, up to 8 MB) for a later write to reference by its id. A file belongs to you, can be used by one write, and expires 24 hours after it was uploaded; an expired or used file is answered like an unknown one.

You may hold 20 files that no write has used yet and that have not expired. Past them it answers 429 file_limit_reached, with details.limit the number you may hold and Retry-After the seconds until the first of them expires; using one of them in a write frees its place at once. A file refused as invalid (422) is refused before they are counted, and a refused upload stores nothing.

Parameters

Name In Type Description
Idempotency-Key Required header string max length 255 A new key for each write, reused only to retry that same write. 1 to 255 printable ASCII characters without spaces (! to ~); any other value answers 400 invalid_idempotency_key.

Request body multipart/form-data, Required

Field Type Description
file Required string (binary)

Responses

  • 201

    The file, stored until expiresAt.

  • 400

    The write has no Idempotency-Key header (code idempotency_key_required), the header is not 1 to 255 printable ASCII characters without spaces (code invalid_idempotency_key), or the body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The key lacks the capability the operation needs (code insufficient_capability), or the account may not perform it (code forbidden).

  • 409

    The Idempotency-Key was used for a different request (code idempotency_key_reused), its first request is still running (code idempotency_request_in_progress), or its first request failed and may have taken effect (code idempotency_outcome_unknown).

  • 422

    No file was sent as the file field, or it is too large, not an odt, doc, docx or txt document, or its name is longer than 255 characters (code validation_failed).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X POST "https://tool.growwer.com/api/v1/files" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -F "[email protected]"

Example response

{
  "id": "00000000-0000-0000-0000-000000000000",
  "name": "string",
  "size": 0,
  "expiresAt": "2026-01-01T00:00:00+00:00"
}

Buyer

Projects

Create, edit and delete the projects you buy for.

GET /api/v1/projects

List your projects

Your projects, newest first.

Parameters

Name In Type Description
limit query integer min 1, max 100, default 20 Projects per page, 1 to 100.
offset query integer min 0, default 0 Projects to skip.

Responses

  • 200

    A page of your projects and how many you have in all.

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 422

    The request is not valid (code validation_failed). details.fields holds the messages for each invalid field, by field name.

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X GET "https://tool.growwer.com/api/v1/projects" \
  -H "Authorization: Bearer $GROWWER_API_KEY"

Example response

{
  "items": [
    {
      "id": "00000000-0000-0000-0000-000000000000",
      "name": "string",
      "rootUrl": "https://example.com",
      "websiteType": "ecommerce",
      "topics": [
        0
      ],
      "language": "es",
      "country": "ES",
      "promotedProducts": [
        "string"
      ],
      "userKeywords": [
        "string"
      ],
      "state": "analyzing",
      "createdAt": "2026-01-01T00:00:00+00:00"
    }
  ],
  "total": 0
}

POST /api/v1/projects

Create a project

Creates a project for a website, as the web's project creation does. Growwer then analyses it in the background to recommend media: the project is answered with state: analyzing and turns finished when the analysis ends. Analysis can take hours, and it never finishes when no medium matches the project, so poll GET /projects/{id} with backoff and use createdAt to decide when to stop.

Every value is validated as on the web: topics, language and country take the identifiers GET /vocabulary publishes.

Creating a project also categorises its website through an external service, as the web does. When that service fails the answer is 500, although the project may already be saved, and a retry under the same Idempotency-Key answers 409 idempotency_outcome_unknown. After either, list your projects (GET /projects) before retrying with a new key, or you may create a duplicate.

Parameters

Name In Type Description
Idempotency-Key Required header string max length 255 A new key for each write, reused only to retry that same write. 1 to 255 printable ASCII characters without spaces (! to ~); any other value answers 400 invalid_idempotency_key.

Request body application/json, Required

Field Type Description
name string | null max length 255
rootUrl Required string (uri)
websiteType Required ecommerce | services | other
topics Required integer[] min items 1, max items 1 Exactly one topic id from GET /vocabulary.
language Required string A language code from GET /vocabulary.
country string | null A country code from GET /vocabulary; omit it or send null for worldwide.
promotedProducts Required string[] min items 5, max items 10 5 to 10 products or services the campaign promotes.
userKeywords string[] max items 100 At most 100 keywords, none with a line break.

Responses

  • 202

    The project, created and being analysed. Location is its URL.

  • 400

    The write has no Idempotency-Key header (code idempotency_key_required), the header is not 1 to 255 printable ASCII characters without spaces (code invalid_idempotency_key), or the body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The key is read-only (code insufficient_capability), or you have reached the number of projects an account may have without a wallet deposit in the last year (code project_limit_reached).

  • 409

    The Idempotency-Key was used for a different request (code idempotency_key_reused), its first request is still running (code idempotency_request_in_progress), or its first request failed and may have taken effect (code idempotency_outcome_unknown).

  • 422

    The request is not valid (code validation_failed). details.fields holds the messages for each invalid field, by field name.

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X POST "https://tool.growwer.com/api/v1/projects" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "string",
  "rootUrl": "https://www.example.com",
  "websiteType": "ecommerce",
  "topics": [
    0
  ],
  "language": "es",
  "country": "ES",
  "promotedProducts": [
    "string 1",
    "string 2",
    "string 3",
    "string 4",
    "string 5"
  ],
  "userKeywords": [
    "string"
  ]
}'

Example response

{
  "id": "00000000-0000-0000-0000-000000000000",
  "name": "string",
  "rootUrl": "https://example.com",
  "websiteType": "ecommerce",
  "topics": [
    0
  ],
  "language": "es",
  "country": "ES",
  "promotedProducts": [
    "string"
  ],
  "userKeywords": [
    "string"
  ],
  "state": "analyzing",
  "createdAt": "2026-01-01T00:00:00+00:00"
}

GET /api/v1/projects/{id}

Get a project

One of your projects. Poll it to follow state after creating the project.

Parameters

Name In Type Description
id Required path string (uuid)

Responses

  • 200

    The project.

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X GET "https://tool.growwer.com/api/v1/projects/$PROJECT_ID" \
  -H "Authorization: Bearer $GROWWER_API_KEY"

Example response

{
  "id": "00000000-0000-0000-0000-000000000000",
  "name": "string",
  "rootUrl": "https://example.com",
  "websiteType": "ecommerce",
  "topics": [
    0
  ],
  "language": "es",
  "country": "ES",
  "promotedProducts": [
    "string"
  ],
  "userKeywords": [
    "string"
  ],
  "state": "analyzing",
  "createdAt": "2026-01-01T00:00:00+00:00"
}

PATCH /api/v1/projects/{id}

Edit a project

Changes only the fields you send. rootUrl cannot be changed; any field not listed here is refused. Changing language, country, promotedProducts or topics makes Growwer categorise the project and work out its target keywords again, in the background.

Parameters

Name In Type Description
id Required path string (uuid)
Idempotency-Key Required header string max length 255 A new key for each write, reused only to retry that same write. 1 to 255 printable ASCII characters without spaces (! to ~); any other value answers 400 invalid_idempotency_key.

Request body application/json, Required

Field Type Description
name string | null max length 255 Null or an empty string removes the name.
websiteType ecommerce | services | other
topics integer[] min items 1, max items 1 Exactly one topic id from GET /vocabulary.
language string A language code from GET /vocabulary.
country string | null A country code from GET /vocabulary, or null for worldwide.
promotedProducts string[] min items 5, max items 10 5 to 10 products or services the campaign promotes.
userKeywords string[] max items 100 At most 100 keywords, none with a line break.

Responses

  • 200

    The project as edited.

  • 400

    The write has no Idempotency-Key header (code idempotency_key_required), the header is not 1 to 255 printable ASCII characters without spaces (code invalid_idempotency_key), or the body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The key lacks the capability the operation needs (code insufficient_capability), or the account may not perform it (code forbidden).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 409

    The Idempotency-Key was used for a different request (code idempotency_key_reused), its first request is still running (code idempotency_request_in_progress), or its first request failed and may have taken effect (code idempotency_outcome_unknown).

  • 422

    The request is not valid (code validation_failed). details.fields holds the messages for each invalid field, by field name.

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X PATCH "https://tool.growwer.com/api/v1/projects/$PROJECT_ID" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "string",
  "websiteType": "ecommerce",
  "topics": [
    0
  ],
  "language": "string",
  "country": "string",
  "promotedProducts": [
    "string 1",
    "string 2",
    "string 3",
    "string 4",
    "string 5"
  ],
  "userKeywords": [
    "string"
  ]
}'

Example response

{
  "id": "00000000-0000-0000-0000-000000000000",
  "name": "string",
  "rootUrl": "https://example.com",
  "websiteType": "ecommerce",
  "topics": [
    0
  ],
  "language": "es",
  "country": "ES",
  "promotedProducts": [
    "string"
  ],
  "userKeywords": [
    "string"
  ],
  "state": "analyzing",
  "createdAt": "2026-01-01T00:00:00+00:00"
}

DELETE /api/v1/projects/{id}

Delete a project

Deletes one of your projects. A project with an order in progress cannot be deleted until its orders finish.

Parameters

Name In Type Description
id Required path string (uuid)
Idempotency-Key Required header string max length 255 A new key for each write, reused only to retry that same write. 1 to 255 printable ASCII characters without spaces (! to ~); any other value answers 400 invalid_idempotency_key.

Responses

  • 204

    The project was deleted.

  • 400

    The write has no Idempotency-Key header (code idempotency_key_required), the header is not 1 to 255 printable ASCII characters without spaces (code invalid_idempotency_key), or the body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The key lacks the capability the operation needs (code insufficient_capability), or the account may not perform it (code forbidden).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 409

    The project has an order in progress (code invalid_state), or the Idempotency-Key conflicts (codes idempotency_key_reused, idempotency_request_in_progress, idempotency_outcome_unknown).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X DELETE "https://tool.growwer.com/api/v1/projects/$PROJECT_ID" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"

Catalogue

Search the medias of a project, its flash offers, favourites and discards.

GET /api/v1/flash-offers

List flash offers

The medias on a flash offer, a time-limited discount, in the catalogue of one of your projects. It is GET /projects/{id}/medias with flashOffer=true, and takes the same parameters except flashOffer and savedFilterId. The catalogue access and the daily limit of that operation apply: each new media costs one unit, a page that holds no new media, an empty one included, costs one, and repeating a request already answered the same day is free while the media lists, topics and language of the project stay as they were.

Parameters

Name In Type Description
projectId Required query string (uuid) The project to search flash offers for.
source query all | recommended | favourite | discarded | requested | bought Which of the project's medias to search: all (the default), recommended (those Growwer recommends for the project), or those you marked favourite, discarded or requested, or bought for the project. Do not combine it with the parameter of the same state.
sort query -growwerRank | postPrice | linkAdditionPrice | matchedAt | -matchedAt | -traffic | -domainRating | linkedToReferringRatio | -referringDomains The order. Without it, the web's default order: medias on the project's topic and language first, then best sellers and medias with more published orders. Ties are always broken by media id, so pages never overlap. -growwerRank: best match first; postPrice, linkAdditionPrice: cheapest first; matchedAt, -matchedAt: when Growwer matched the media with the project; -traffic, -domainRating, -referringDomains: highest first; linkedToReferringRatio: lowest first.
domain query string Medias whose domain contains this text, 1 to 255 characters.
websiteType query string A comma-separated list of website types: blog, press.
language query string A comma-separated list of language codes, as in GET /vocabulary.
country query string A comma-separated list of country codes, as in GET /vocabulary.
topics query string A comma-separated list of topic ids, as in GET /vocabulary: medias on any of them.
notBannedTopics query boolean Leave out medias that refuse the topic of the project. Only true applies it; omit it otherwise.
minTraffic query integer Minimum monthly organic traffic.
maxTraffic query integer Maximum monthly organic traffic.
trafficTrendUpward query boolean Medias whose traffic is growing. Only true applies it; omit it otherwise.
minDomainRating query integer Minimum domain rating, 0 to 100.
maxDomainRating query integer Maximum domain rating, 0 to 100.
minLinkedToReferringRatio query number Minimum ratio of linked domains to referring domains, such as 0.5.
maxLinkedToReferringRatio query number Maximum ratio of linked domains to referring domains, such as 1.5.
minPostPrice query integer Minimum post price, in cents.
maxPostPrice query integer Maximum post price, in cents.
postLinkType query string A comma-separated list of link types the post offer gives: dofollow, nofollow, citation.
promotedSocialNetwork query string A comma-separated list of social networks the media promotes posts on: twitter, instagram, facebook, pinterest, tumblr, newsletter.
withoutAdvertisingLabel query boolean Medias that never label posts as advertising. Only true applies it; omit it otherwise.
homepageListing query boolean Medias that offer listing the post on their homepage. Only true applies it; omit it otherwise.
hasLinkAddition query boolean Medias that offer adding a link to an existing article. Only true applies it; omit it otherwise.
minLinkAdditionPrice query integer Minimum link addition price, in cents.
maxLinkAdditionPrice query integer Maximum link addition price, in cents.
linkAdditionLinkType query string A comma-separated list of link types the link addition offer gives: dofollow, nofollow, citation.
recommended query boolean true: only the medias Growwer recommends for the project; false: only the others.
favourite query boolean true: only your favourites for the project; false: leave them out.
requested query boolean true: only the medias you requested for the project; false: leave them out.
bought query boolean true: only medias you bought for the project; false: leave them out.
discarded query boolean true: only the medias you discarded for the project; false: leave them out.
new query boolean Medias Growwer matched with the project recently. Only true applies it; omit it otherwise.
ranksGoalKeyword query string A comma-separated list of keyword origins: medias that rank for a keyword of the project from any of them. user: your keywords; publicGenerated: keywords Growwer found for the project.
ranksAiKeyword query string A comma-separated list of AI answer engines: medias the web lists an AI keyword of the project for, from any of them. googleAiOverview, chatgpt, googleAiMode.
limit query integer min 1, max 100, default 20 Medias per page, 1 to 100.
offset query integer min 0, max 99900, default 0 Medias to skip, at most 99900.

Responses

  • 200

    A page of flash offers and how many there are in all.

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The catalogue is open to accounts with recent completed orders, as buyer or as seller, and to other accounts within a daily trial quota; this account has no such order and the trial quota is off (code catalogue_requires_recent_order). GET /me reports catalogue.eligible and catalogue.dailyCap.

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 422

    The request is not valid (code validation_failed). details.fields holds the messages for each invalid field, by field name.

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X GET "https://tool.growwer.com/api/v1/flash-offers?projectId=$PROJECT_ID" \
  -H "Authorization: Bearer $GROWWER_API_KEY"

GET /api/v1/medias/{id}

Get a media

One media of the catalogue: an active, public media. A media you have ordered is always readable, even if it has since left the catalogue, and is open to every account. Otherwise the media counts towards the distinct medias the account may see per day (UTC): the full daily cap for accounts with recent completed orders, a smaller trial quota for other accounts (see catalogue.eligible, catalogue.dailyCap and catalogue.remainingToday on GET /me). For the media's state in one of your projects, search the project with GET /projects/{id}/medias.

Parameters

Name In Type Description
id Required path string (uuid) The media.

Responses

  • 200

    The media.

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The catalogue is open to accounts with recent completed orders, as buyer or as seller, and to other accounts within a daily trial quota; this account has no such order and the trial quota is off (code catalogue_requires_recent_order). GET /me reports catalogue.eligible and catalogue.dailyCap.

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X GET "https://tool.growwer.com/api/v1/medias/$MEDIA_ID" \
  -H "Authorization: Bearer $GROWWER_API_KEY"

Example response

{
  "id": "00000000-0000-0000-0000-000000000000",
  "domain": "example.com",
  "type": "blog",
  "language": "string",
  "country": "string",
  "topics": [
    0
  ],
  "metrics": {
    "organicTraffic": 0,
    "domainRating": 0,
    "referringDomains": 0,
    "linkedDomains": 0,
    "linkedToReferringRatio": 0
  },
  "positiveTrafficTrend": false,
  "bestSeller": false,
  "publicationRate": 0,
  "averageDaysToPublication": 0,
  "onHoliday": false,
  "flashOffer": {
    "discountPercent": 0,
    "endsAt": "2026-01-01T00:00:00+00:00"
  },
  "postOffer": {
    "price": {
      "amount": 12500,
      "currency": "EUR"
    },
    "linkType": "dofollow",
    "prices": {},
    "previousPrice": {
      "amount": 12500,
      "currency": "EUR"
    },
    "advertisingLabel": false,
    "promotedSocialNetworks": [
      "twitter"
    ],
    "extras": {
      "homepageListing": {
        "amount": 12500,
        "currency": "EUR"
      },
      "sensitiveTopic": {
        "allowed": false,
        "extraPrice": {
          "amount": null,
          "currency": null
        }
      }
    }
  },
  "linkAdditionOffer": {
    "price": {
      "amount": 12500,
      "currency": "EUR"
    },
    "linkType": "dofollow",
    "prices": {},
    "previousPrice": {
      "amount": 12500,
      "currency": "EUR"
    }
  }
}

PUT /api/v1/projects/{id}/discards/{mediaId}

Discard a media for a project

Discards a media for one of your projects, as the web does. Discarding it again changes nothing. The media must be one the catalogue shows for the project: another media, including one of your own as a seller, is answered as not found.

Parameters

Name In Type Description
id Required path string (uuid) The project.
mediaId Required path string (uuid) The media.
Idempotency-Key Required header string max length 255 A new key for each write, reused only to retry that same write. 1 to 255 printable ASCII characters without spaces (! to ~); any other value answers 400 invalid_idempotency_key.

Responses

  • 204

    The media is discarded for the project.

  • 400

    The write has no Idempotency-Key header (code idempotency_key_required), the header is not 1 to 255 printable ASCII characters without spaces (code invalid_idempotency_key), or the body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The key lacks the capability the operation needs (code insufficient_capability), or the account may not perform it (code forbidden).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 409

    The Idempotency-Key was used for a different request (code idempotency_key_reused), its first request is still running (code idempotency_request_in_progress), or its first request failed and may have taken effect (code idempotency_outcome_unknown).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X PUT "https://tool.growwer.com/api/v1/projects/$PROJECT_ID/discards/$MEDIA_ID" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"

DELETE /api/v1/projects/{id}/discards/{mediaId}

Restore a discarded media for a project

Restores a media you discarded for one of your projects. Restoring one that is not discarded changes nothing. It works for any media, even one the catalogue no longer shows.

Parameters

Name In Type Description
id Required path string (uuid) The project.
mediaId Required path string (uuid) The media.
Idempotency-Key Required header string max length 255 A new key for each write, reused only to retry that same write. 1 to 255 printable ASCII characters without spaces (! to ~); any other value answers 400 invalid_idempotency_key.

Responses

  • 204

    The media is not discarded for the project.

  • 400

    The write has no Idempotency-Key header (code idempotency_key_required), the header is not 1 to 255 printable ASCII characters without spaces (code invalid_idempotency_key), or the body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The key lacks the capability the operation needs (code insufficient_capability), or the account may not perform it (code forbidden).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 409

    The Idempotency-Key was used for a different request (code idempotency_key_reused), its first request is still running (code idempotency_request_in_progress), or its first request failed and may have taken effect (code idempotency_outcome_unknown).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X DELETE "https://tool.growwer.com/api/v1/projects/$PROJECT_ID/discards/$MEDIA_ID" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"

PUT /api/v1/projects/{id}/favourites/{mediaId}

Add a media to the favourites of a project

Marks a media as a favourite of one of your projects, as the heart on the web does. Adding a favourite again changes nothing. The media must be one the catalogue shows for the project: another media, including one of your own as a seller, is answered as not found.

Parameters

Name In Type Description
id Required path string (uuid) The project.
mediaId Required path string (uuid) The media.
Idempotency-Key Required header string max length 255 A new key for each write, reused only to retry that same write. 1 to 255 printable ASCII characters without spaces (! to ~); any other value answers 400 invalid_idempotency_key.

Responses

  • 204

    The media is a favourite of the project.

  • 400

    The write has no Idempotency-Key header (code idempotency_key_required), the header is not 1 to 255 printable ASCII characters without spaces (code invalid_idempotency_key), or the body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The key lacks the capability the operation needs (code insufficient_capability), or the account may not perform it (code forbidden).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 409

    The Idempotency-Key was used for a different request (code idempotency_key_reused), its first request is still running (code idempotency_request_in_progress), or its first request failed and may have taken effect (code idempotency_outcome_unknown).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X PUT "https://tool.growwer.com/api/v1/projects/$PROJECT_ID/favourites/$MEDIA_ID" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"

DELETE /api/v1/projects/{id}/favourites/{mediaId}

Remove a media from the favourites of a project

Removes a media from the favourites of one of your projects. Removing one that is not a favourite changes nothing. It works for any media, even one the catalogue no longer shows.

Parameters

Name In Type Description
id Required path string (uuid) The project.
mediaId Required path string (uuid) The media.
Idempotency-Key Required header string max length 255 A new key for each write, reused only to retry that same write. 1 to 255 printable ASCII characters without spaces (! to ~); any other value answers 400 invalid_idempotency_key.

Responses

  • 204

    The media is not a favourite of the project.

  • 400

    The write has no Idempotency-Key header (code idempotency_key_required), the header is not 1 to 255 printable ASCII characters without spaces (code invalid_idempotency_key), or the body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The key lacks the capability the operation needs (code insufficient_capability), or the account may not perform it (code forbidden).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 409

    The Idempotency-Key was used for a different request (code idempotency_key_reused), its first request is still running (code idempotency_request_in_progress), or its first request failed and may have taken effect (code idempotency_outcome_unknown).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X DELETE "https://tool.growwer.com/api/v1/projects/$PROJECT_ID/favourites/$MEDIA_ID" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"

POST /api/v1/projects/{id}/media-requests

Request specific medias for a project

Asks Growwer to find the medias of these domains for one of your projects, as the "request specific domains" form on the web does. Each entry may be a domain or a URL; it is read as its full domain, lowercased. Repeated domains are requested once, and a domain already requested changes nothing. Up to 200 distinct domains per call. They then appear in the project's catalogue with project.requested true (requested=true on GET /projects/{id}/medias) once Growwer lists them.

Parameters

Name In Type Description
id Required path string (uuid) The project.
Idempotency-Key Required header string max length 255 A new key for each write, reused only to retry that same write. 1 to 255 printable ASCII characters without spaces (! to ~); any other value answers 400 invalid_idempotency_key.

Request body application/json, Required

Field Type Description
domains Required string[] Domains or URLs, 1 to 200 distinct domains.

Responses

  • 201

    The domains were requested: each distinct domain, as Growwer read it.

  • 400

    The write has no Idempotency-Key header (code idempotency_key_required), the header is not 1 to 255 printable ASCII characters without spaces (code invalid_idempotency_key), or the body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The key lacks the capability the operation needs (code insufficient_capability), or the account may not perform it (code forbidden).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 409

    The Idempotency-Key was used for a different request (code idempotency_key_reused), its first request is still running (code idempotency_request_in_progress), or its first request failed and may have taken effect (code idempotency_outcome_unknown).

  • 422

    The request is not valid (code validation_failed). details.fields holds the messages for each invalid field, by field name.

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X POST "https://tool.growwer.com/api/v1/projects/$PROJECT_ID/media-requests" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "domains": [
    "example.com"
  ]
}'

Example response

{
  "domains": [
    "string"
  ]
}

GET /api/v1/projects/{id}/medias

Search the catalogue for a project

The medias the catalogue offers for one of your projects, as the web lists them, with each media's state in the project. Filter with the parameters below, which all combine, or apply one of your saved filters with savedFilterId alone. Each account can see a limited number of distinct medias per day (UTC) across the catalogue operations: the full daily cap for accounts with recent completed orders, as buyer or as seller, and a smaller trial quota for other accounts, which rises to the full cap after a completed order (see catalogue.eligible, catalogue.dailyCap and catalogue.remainingToday on GET /me). Each new media of a page costs one unit of that limit, and a page that holds no new media, an empty one included, costs one unit, so a total costs at least one; repeating a request already answered the same day is free, and so are medias seen again that day. A change of the media lists of the project (a media request, a favourite, a discard), its topics or its language makes the same request a new one.

Parameters

Name In Type Description
id Required path string (uuid) The project.
source query all | recommended | favourite | discarded | requested | bought Which of the project's medias to search: all (the default), recommended (those Growwer recommends for the project), or those you marked favourite, discarded or requested, or bought for the project. Do not combine it with the parameter of the same state.
sort query -growwerRank | postPrice | linkAdditionPrice | matchedAt | -matchedAt | -traffic | -domainRating | linkedToReferringRatio | -referringDomains The order. Without it, the web's default order: medias on the project's topic and language first, then best sellers and medias with more published orders. Ties are always broken by media id, so pages never overlap. -growwerRank: best match first; postPrice, linkAdditionPrice: cheapest first; matchedAt, -matchedAt: when Growwer matched the media with the project; -traffic, -domainRating, -referringDomains: highest first; linkedToReferringRatio: lowest first.
savedFilterId query string (uuid) Apply one of your saved filters of this project, including one saved on the web, instead of the other parameters. Do not combine it with any filter, source or sort. A filter saved on the web that uses a filter the API does not support is refused with 422 saved_filter_unsupported.
domain query string Medias whose domain contains this text, 1 to 255 characters.
websiteType query string A comma-separated list of website types: blog, press.
language query string A comma-separated list of language codes, as in GET /vocabulary.
country query string A comma-separated list of country codes, as in GET /vocabulary.
topics query string A comma-separated list of topic ids, as in GET /vocabulary: medias on any of them.
notBannedTopics query boolean Leave out medias that refuse the topic of the project. Only true applies it; omit it otherwise.
minTraffic query integer Minimum monthly organic traffic.
maxTraffic query integer Maximum monthly organic traffic.
trafficTrendUpward query boolean Medias whose traffic is growing. Only true applies it; omit it otherwise.
minDomainRating query integer Minimum domain rating, 0 to 100.
maxDomainRating query integer Maximum domain rating, 0 to 100.
minLinkedToReferringRatio query number Minimum ratio of linked domains to referring domains, such as 0.5.
maxLinkedToReferringRatio query number Maximum ratio of linked domains to referring domains, such as 1.5.
minPostPrice query integer Minimum post price, in cents.
maxPostPrice query integer Maximum post price, in cents.
postLinkType query string A comma-separated list of link types the post offer gives: dofollow, nofollow, citation.
promotedSocialNetwork query string A comma-separated list of social networks the media promotes posts on: twitter, instagram, facebook, pinterest, tumblr, newsletter.
withoutAdvertisingLabel query boolean Medias that never label posts as advertising. Only true applies it; omit it otherwise.
homepageListing query boolean Medias that offer listing the post on their homepage. Only true applies it; omit it otherwise.
hasLinkAddition query boolean Medias that offer adding a link to an existing article. Only true applies it; omit it otherwise.
minLinkAdditionPrice query integer Minimum link addition price, in cents.
maxLinkAdditionPrice query integer Maximum link addition price, in cents.
linkAdditionLinkType query string A comma-separated list of link types the link addition offer gives: dofollow, nofollow, citation.
recommended query boolean true: only the medias Growwer recommends for the project; false: only the others.
favourite query boolean true: only your favourites for the project; false: leave them out.
requested query boolean true: only the medias you requested for the project; false: leave them out.
flashOffer query boolean true: only medias with a flash offer running; false: leave them out.
bought query boolean true: only medias you bought for the project; false: leave them out.
discarded query boolean true: only the medias you discarded for the project; false: leave them out.
new query boolean Medias Growwer matched with the project recently. Only true applies it; omit it otherwise.
ranksGoalKeyword query string A comma-separated list of keyword origins: medias that rank for a keyword of the project from any of them. user: your keywords; publicGenerated: keywords Growwer found for the project.
ranksAiKeyword query string A comma-separated list of AI answer engines: medias the web lists an AI keyword of the project for, from any of them. googleAiOverview, chatgpt, googleAiMode.
limit query integer min 1, max 100, default 20 Medias per page, 1 to 100.
offset query integer min 0, max 99900, default 0 Medias to skip, at most 99900.

Responses

  • 200

    A page of matching medias and how many match in all.

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The catalogue is open to accounts with recent completed orders, as buyer or as seller, and to other accounts within a daily trial quota; this account has no such order and the trial quota is off (code catalogue_requires_recent_order). GET /me reports catalogue.eligible and catalogue.dailyCap.

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 422

    A parameter is not valid (code validation_failed; details.fields names it), or the saved filter uses a web filter the API does not support (code saved_filter_unsupported; details.field names it).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X GET "https://tool.growwer.com/api/v1/projects/$PROJECT_ID/medias" \
  -H "Authorization: Bearer $GROWWER_API_KEY"

Saved filters

Save catalogue searches of a project and count their medias.

GET /api/v1/projects/{id}/saved-filters

List the saved filters of a project

Your saved filters of one of your projects, newest first, including those saved on the web. A project has at most 100. Apply one with savedFilterId on GET /projects/{id}/medias.

Parameters

Name In Type Description
id Required path string (uuid) The project.

Responses

  • 200

    The saved filters and how many there are.

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X GET "https://tool.growwer.com/api/v1/projects/$PROJECT_ID/saved-filters" \
  -H "Authorization: Bearer $GROWWER_API_KEY"

Example response

{
  "items": [
    {
      "id": "00000000-0000-0000-0000-000000000000",
      "name": "string",
      "activeFilterCount": 0,
      "notifications": {
        "enabled": false,
        "frequency": "instant"
      },
      "createdAt": "2026-01-01T00:00:00+00:00"
    }
  ],
  "total": 0
}

POST /api/v1/projects/{id}/saved-filters

Save a filter

Saves a catalogue search of one of your projects, which then also appears on the web. filters takes the filter parameters of GET /projects/{id}/medias, and source, with the same values; lists may be JSON arrays and flags JSON booleans. With notificationFrequency, Growwer emails you new medias that match. A project has at most 100 saved filters and an account 500.

Parameters

Name In Type Description
id Required path string (uuid) The project.
Idempotency-Key Required header string max length 255 A new key for each write, reused only to retry that same write. 1 to 255 printable ASCII characters without spaces (! to ~); any other value answers 400 invalid_idempotency_key.

Request body application/json, Required

Field Type Description
name Required string max length 255 1 to 255 characters.
filters Required object At least one filter parameter of GET /projects/{id}/medias, or source, by name.
notificationFrequency daily | weekly | monthly Email new matching medias this often. Omit it for no emails.

Responses

  • 201

    The saved filter.

  • 400

    The write has no Idempotency-Key header (code idempotency_key_required), the header is not 1 to 255 printable ASCII characters without spaces (code invalid_idempotency_key), or the body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The key lacks the capability the operation needs (code insufficient_capability), or the account may not perform it (code forbidden).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 409

    The project already has 100 saved filters, or the account 500 (code saved_filter_limit_reached), or the Idempotency-Key conflicts (codes idempotency_key_reused, idempotency_request_in_progress, idempotency_outcome_unknown).

  • 422

    The request is not valid (code validation_failed). details.fields holds the messages for each invalid field, by field name.

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X POST "https://tool.growwer.com/api/v1/projects/$PROJECT_ID/saved-filters" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "string",
  "filters": {
    "language": [
      "es"
    ],
    "minDomainRating": 30,
    "favourite": true
  },
  "notificationFrequency": "daily"
}'

Example response

{
  "id": "00000000-0000-0000-0000-000000000000",
  "name": "string",
  "activeFilterCount": 0,
  "notifications": {
    "enabled": false,
    "frequency": "instant"
  },
  "createdAt": "2026-01-01T00:00:00+00:00"
}

DELETE /api/v1/projects/{id}/saved-filters/{filterId}

Delete a saved filter

Deletes one of your saved filters of one of your projects, including one saved on the web. Its notifications stop.

Parameters

Name In Type Description
id Required path string (uuid) The project.
filterId Required path string (uuid) The saved filter.
Idempotency-Key Required header string max length 255 A new key for each write, reused only to retry that same write. 1 to 255 printable ASCII characters without spaces (! to ~); any other value answers 400 invalid_idempotency_key.

Responses

  • 204

    The saved filter was deleted.

  • 400

    The write has no Idempotency-Key header (code idempotency_key_required), the header is not 1 to 255 printable ASCII characters without spaces (code invalid_idempotency_key), or the body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The key lacks the capability the operation needs (code insufficient_capability), or the account may not perform it (code forbidden).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 409

    The Idempotency-Key was used for a different request (code idempotency_key_reused), its first request is still running (code idempotency_request_in_progress), or its first request failed and may have taken effect (code idempotency_outcome_unknown).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X DELETE "https://tool.growwer.com/api/v1/projects/$PROJECT_ID/saved-filters/$FILTER_ID" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"

GET /api/v1/projects/{id}/saved-filters/{filterId}/media-count

Count the medias of a saved filter

How many medias of the project's catalogue one of your saved filters matches: the total of GET /projects/{id}/medias with its savedFilterId. The number is cached for up to 24 hours, and it can differ from the count the web shows, which is not limited to the project's catalogue. A filter saved on the web that uses a filter the API does not support is refused with 422 saved_filter_unsupported. The catalogue access and the daily limit of the catalogue apply: a count costs one unit of that limit the first time a filter is counted in a UTC day, and counting the same filter again that day is free.

Parameters

Name In Type Description
id Required path string (uuid) The project.
filterId Required path string (uuid) The saved filter.

Responses

  • 200

    The number of matching medias.

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The catalogue is open to accounts with recent completed orders, as buyer or as seller, and to other accounts within a daily trial quota; this account has no such order and the trial quota is off (code catalogue_requires_recent_order). GET /me reports catalogue.eligible and catalogue.dailyCap.

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 422

    The saved filter uses a web filter the API does not support (code saved_filter_unsupported); details.field names it.

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X GET "https://tool.growwer.com/api/v1/projects/$PROJECT_ID/saved-filters/$FILTER_ID/media-count" \
  -H "Authorization: Bearer $GROWWER_API_KEY"

Example response

{
  "count": 0
}

Buyer orders

Estimate, place and follow the orders you buy, and act on them.

GET /api/v1/orders

List your orders

The orders you bought, newest first by default. To follow your orders, poll with sort=updatedAt and updatedSince set to the updatedAt of the last order you saw: an order moves to the end of that list each time it changes state.

Parameters

Name In Type Description
projectId query string (uuid) Only the orders of this project of yours.
mediaId query string (uuid) Only the orders on this media.
state query string Only the orders in one of these states, comma-separated. An unknown state is refused with 422 and the list of valid ones.
updatedSince query string (date-time) Only the orders whose updatedAt is at or after this instant, an ISO-8601 date-time with its offset (send + as %2B).
sort query -createdAt | updatedAt default "-createdAt" -createdAt, newest first, or updatedAt, least recently changed first. Orders of the same instant come in id order.
limit query integer min 1, max 100, default 20 Orders per page, 1 to 100.
offset query integer min 0, default 0 Orders to skip.

Responses

  • 200

    A page of your orders and how many match in all.

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 422

    The request is not valid (code validation_failed). details.fields holds the messages for each invalid field, by field name.

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X GET "https://tool.growwer.com/api/v1/orders" \
  -H "Authorization: Bearer $GROWWER_API_KEY"

Example response

{
  "items": [
    {
      "id": "00000000-0000-0000-0000-000000000000",
      "reference": "string",
      "type": "post",
      "state": "string",
      "pendingAction": "buyer",
      "price": {
        "amount": 12500,
        "currency": "EUR"
      },
      "project": {
        "id": "00000000-0000-0000-0000-000000000000",
        "url": "string"
      },
      "media": {
        "id": "00000000-0000-0000-0000-000000000000",
        "url": "string"
      },
      "createdAt": "2026-01-01T00:00:00+00:00",
      "updatedAt": "2026-01-01T00:00:00+00:00",
      "scheduledPublicationDate": "2026-01-01T00:00:00+00:00",
      "delivery": {
        "url": "string",
        "publishedAt": "2026-01-01T00:00:00+00:00"
      },
      "briefing": {
        "title": "string",
        "language": "string",
        "links": [],
        "extraServices": [
          "list_on_homepage"
        ],
        "additionalInfo": "string",
        "hasProposedDraft": false
      },
      "draft": {
        "authoredByBuyer": false,
        "reviewable": false,
        "hasFile": false
      },
      "stateHistory": [
        {
          "state": null,
          "date": null,
          "reason": null
        }
      ],
      "channel": {
        "created": "api"
      }
    }
  ],
  "total": 0
}

POST /api/v1/orders

Place an order

Places an order on a media for one of your projects and pays its price from your wallet's available balance, as ordering on the web does. Estimate the order first (POST /orders/estimate) and send the price it answers as expectedPrice: the order is placed only at that price.

Placing the same order (same project, media, type and price) again within 5 seconds of one still in progress is refused with duplicate_order. Its details.orderId is the earlier order, or null when that order has finished or was deleted since.

A proposed draft (proposedDraftFileId, from POST /files) is used up when the order is placed. If the order is then refused because another request changed your balance, the media or its price, or placed the same order, the file is used up all the same and the refusal's message says so: upload it again before retrying.

Parameters

Name In Type Description
Idempotency-Key Required header string max length 255 A new key for each write, reused only to retry that same write. 1 to 255 printable ASCII characters without spaces (! to ~); any other value answers 400 invalid_idempotency_key.

Request body application/json, Required

Field Type Description
expectedPrice Required Money The price POST /orders/estimate answered. Only EUR is accepted.

Responses

  • 201

    The order, placed and paid. Location is its URL.

  • 400

    The write has no Idempotency-Key header (code idempotency_key_required), the header is not 1 to 255 printable ASCII characters without spaces (code invalid_idempotency_key), or the body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The key is read-only (code insufficient_capability).

  • 404

    The project is not yours, or the media does not exist or is not one you can order for this project (code not_found).

  • 409

    The order cannot be placed, and nothing was charged:

    • price_changed: the price is not expectedPrice; details.currentPrice is the price.
    • insufficient_balance: your available balance does not cover the price; details has required, available and missing (Money).
    • duplicate_order: the same order was placed seconds ago; details.orderId is that order, or null.
    • seller_on_holiday: the media is not taking orders right now.
    • media_topic_not_allowed: the media does not accept the project's topic.
    • offer_unavailable: the media does not offer this type of order.
    • idempotency_key_reused, idempotency_request_in_progress, idempotency_outcome_unknown: the Idempotency-Key conflicts.
  • 422

    The body is not valid (code validation_failed, with the messages of each field in details.fields), or proposedDraftFileId names no file you can use: it does not exist, has expired or was already used (code file_unusable).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X POST "https://tool.growwer.com/api/v1/orders" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "type": "post",
  "projectId": "00000000-0000-0000-0000-000000000000",
  "mediaId": "00000000-0000-0000-0000-000000000000",
  "briefing": {
    "links": [
      {
        "targetUrl": "string",
        "anchor": "string",
        "type": "dofollow"
      }
    ],
    "extraServices": [
      "list_on_homepage"
    ],
    "title": "string",
    "additionalInfo": "string"
  },
  "scheduledPublicationDate": "2026-01-01",
  "draftReviewable": false,
  "proposedDraftFileId": "00000000-0000-0000-0000-000000000000",
  "expectedPrice": {
    "amount": 12500,
    "currency": "EUR"
  }
}'

Example response

{
  "id": "00000000-0000-0000-0000-000000000000",
  "reference": "string",
  "type": "post",
  "state": "string",
  "pendingAction": "buyer",
  "price": {
    "amount": 12500,
    "currency": "EUR"
  },
  "project": {
    "id": "00000000-0000-0000-0000-000000000000",
    "url": "string"
  },
  "media": {
    "id": "00000000-0000-0000-0000-000000000000",
    "url": "string"
  },
  "createdAt": "2026-01-01T00:00:00+00:00",
  "updatedAt": "2026-01-01T00:00:00+00:00",
  "scheduledPublicationDate": "2026-01-01T00:00:00+00:00",
  "delivery": {
    "url": "string",
    "publishedAt": "2026-01-01T00:00:00+00:00"
  },
  "briefing": {
    "title": "string",
    "language": "string",
    "links": [
      {
        "targetUrl": "string",
        "anchor": "string",
        "type": "dofollow"
      }
    ],
    "extraServices": [
      "list_on_homepage"
    ],
    "additionalInfo": "string",
    "hasProposedDraft": false
  },
  "draft": {
    "authoredByBuyer": false,
    "reviewable": false,
    "hasFile": false
  },
  "stateHistory": [
    {
      "state": "string",
      "date": "2026-01-01T00:00:00+00:00",
      "reason": {
        "code": "other_reasons",
        "additionalInfo": "string"
      }
    }
  ],
  "channel": {
    "created": "api"
  }
}

POST /api/v1/orders/estimate

Estimate an order

Checks and prices the order POST /orders would place with the same body, without expectedPrice, and compares the price with your wallet's available balance. Nothing is placed, charged or used up, so a read-only key may call it and it takes no Idempotency-Key. Send the price it answers as expectedPrice when you place the order.

The estimate is refused for the same reasons as the order itself, except a balance that does not cover the price: that is answered here with sufficientBalance: false and the missing amount.

Request body application/json, Required

Field Type Description
type Required post | link_addition
projectId Required string (uuid) One of your projects.
mediaId Required string (uuid) A media you can order for the project (GET /projects/{id}/medias).
briefing Required PostOrderBriefingRequest | LinkAdditionOrderBriefingRequest
scheduledPublicationDate string (date) The day to publish on, at least a few days ahead as the web requires.
draftReviewable boolean Whether you review the draft before it is published. Defaults to false.
proposedDraftFileId string (uuid) A file uploaded with POST /files to propose your own draft. The estimate checks it without using it up.

Responses

  • 200

    The price of the order and how your wallet covers it.

  • 400

    The body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 404

    The project is not yours, or the media does not exist or is not one you can order for this project (code not_found).

  • 409

    The order could not be placed:

    • seller_on_holiday: the media is not taking orders right now.
    • media_topic_not_allowed: the media does not accept the project's topic.
    • offer_unavailable: the media does not offer this type of order.
  • 422

    The body is not valid (code validation_failed, with the messages of each field in details.fields), or proposedDraftFileId names no file you can use: it does not exist, has expired or was already used (code file_unusable).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X POST "https://tool.growwer.com/api/v1/orders/estimate" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "type": "post",
  "projectId": "00000000-0000-0000-0000-000000000000",
  "mediaId": "00000000-0000-0000-0000-000000000000",
  "briefing": {
    "links": [
      {
        "targetUrl": "string",
        "anchor": "string",
        "type": "dofollow"
      }
    ],
    "extraServices": [
      "list_on_homepage"
    ],
    "title": "string",
    "additionalInfo": "string"
  },
  "scheduledPublicationDate": "2026-01-01",
  "draftReviewable": false,
  "proposedDraftFileId": "00000000-0000-0000-0000-000000000000"
}'

Example response

{
  "price": {
    "amount": 12500,
    "currency": "EUR"
  },
  "availableBalance": {
    "amount": 12500,
    "currency": "EUR"
  },
  "missing": {
    "amount": 12500,
    "currency": "EUR"
  },
  "sufficientBalance": false
}

GET /api/v1/orders/{id}

Get an order

One order you bought, with its briefing, its draft and its state history.

Parameters

Name In Type Description
id Required path string (uuid)

Responses

  • 200

    The order.

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X GET "https://tool.growwer.com/api/v1/orders/$ORDER_ID" \
  -H "Authorization: Bearer $GROWWER_API_KEY"

Example response

{
  "id": "00000000-0000-0000-0000-000000000000",
  "reference": "string",
  "type": "post",
  "state": "string",
  "pendingAction": "buyer",
  "price": {
    "amount": 12500,
    "currency": "EUR"
  },
  "project": {
    "id": "00000000-0000-0000-0000-000000000000",
    "url": "string"
  },
  "media": {
    "id": "00000000-0000-0000-0000-000000000000",
    "url": "string"
  },
  "createdAt": "2026-01-01T00:00:00+00:00",
  "updatedAt": "2026-01-01T00:00:00+00:00",
  "scheduledPublicationDate": "2026-01-01T00:00:00+00:00",
  "delivery": {
    "url": "string",
    "publishedAt": "2026-01-01T00:00:00+00:00"
  },
  "briefing": {
    "title": "string",
    "language": "string",
    "links": [
      {
        "targetUrl": "string",
        "anchor": "string",
        "type": "dofollow"
      }
    ],
    "extraServices": [
      "list_on_homepage"
    ],
    "additionalInfo": "string",
    "hasProposedDraft": false
  },
  "draft": {
    "authoredByBuyer": false,
    "reviewable": false,
    "hasFile": false
  },
  "stateHistory": [
    {
      "state": "string",
      "date": "2026-01-01T00:00:00+00:00",
      "reason": {
        "code": "other_reasons",
        "additionalInfo": "string"
      }
    }
  ],
  "channel": {
    "created": "api"
  }
}

POST /api/v1/orders/{id}/approve

Approve an order's delivery

Approves the delivery of an order whose pendingAction is yours to review the delivery, as the web does. The order finishes and the seller is paid. It takes no body.

Parameters

Name In Type Description
id Required path string (uuid)
Idempotency-Key Required header string max length 255 A new key for each write, reused only to retry that same write. 1 to 255 printable ASCII characters without spaces (! to ~); any other value answers 400 invalid_idempotency_key.

Responses

  • 200

    The order, approved.

  • 400

    The write has no Idempotency-Key header (code idempotency_key_required), the header is not 1 to 255 printable ASCII characters without spaces (code invalid_idempotency_key), or the body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The key is read-only (code insufficient_capability).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 409

    The order is not pending your review of its delivery (code invalid_state), or the Idempotency-Key conflicts (codes idempotency_key_reused, idempotency_request_in_progress, idempotency_outcome_unknown).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X POST "https://tool.growwer.com/api/v1/orders/$ORDER_ID/approve" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"

Example response

{
  "id": "00000000-0000-0000-0000-000000000000",
  "reference": "string",
  "type": "post",
  "state": "string",
  "pendingAction": "buyer",
  "price": {
    "amount": 12500,
    "currency": "EUR"
  },
  "project": {
    "id": "00000000-0000-0000-0000-000000000000",
    "url": "string"
  },
  "media": {
    "id": "00000000-0000-0000-0000-000000000000",
    "url": "string"
  },
  "createdAt": "2026-01-01T00:00:00+00:00",
  "updatedAt": "2026-01-01T00:00:00+00:00",
  "scheduledPublicationDate": "2026-01-01T00:00:00+00:00",
  "delivery": {
    "url": "string",
    "publishedAt": "2026-01-01T00:00:00+00:00"
  },
  "briefing": {
    "title": "string",
    "language": "string",
    "links": [
      {
        "targetUrl": "string",
        "anchor": "string",
        "type": "dofollow"
      }
    ],
    "extraServices": [
      "list_on_homepage"
    ],
    "additionalInfo": "string",
    "hasProposedDraft": false
  },
  "draft": {
    "authoredByBuyer": false,
    "reviewable": false,
    "hasFile": false
  },
  "stateHistory": [
    {
      "state": "string",
      "date": "2026-01-01T00:00:00+00:00",
      "reason": {
        "code": "other_reasons",
        "additionalInfo": "string"
      }
    }
  ],
  "channel": {
    "created": "api"
  }
}

POST /api/v1/orders/{id}/approve-draft

Approve a draft

Approves the draft of a post order whose pendingAction is yours to review the draft (GET /orders/{id}/draft downloads it), as the web does. The seller then publishes it. It takes no body.

Parameters

Name In Type Description
id Required path string (uuid)
Idempotency-Key Required header string max length 255 A new key for each write, reused only to retry that same write. 1 to 255 printable ASCII characters without spaces (! to ~); any other value answers 400 invalid_idempotency_key.

Responses

  • 200

    The order, with its draft approved.

  • 400

    The write has no Idempotency-Key header (code idempotency_key_required), the header is not 1 to 255 printable ASCII characters without spaces (code invalid_idempotency_key), or the body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The key is read-only (code insufficient_capability).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 409

    The order is not a post order with a draft pending your review (code invalid_state), or the Idempotency-Key conflicts (codes idempotency_key_reused, idempotency_request_in_progress, idempotency_outcome_unknown).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X POST "https://tool.growwer.com/api/v1/orders/$ORDER_ID/approve-draft" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"

Example response

{
  "id": "00000000-0000-0000-0000-000000000000",
  "reference": "string",
  "type": "post",
  "state": "string",
  "pendingAction": "buyer",
  "price": {
    "amount": 12500,
    "currency": "EUR"
  },
  "project": {
    "id": "00000000-0000-0000-0000-000000000000",
    "url": "string"
  },
  "media": {
    "id": "00000000-0000-0000-0000-000000000000",
    "url": "string"
  },
  "createdAt": "2026-01-01T00:00:00+00:00",
  "updatedAt": "2026-01-01T00:00:00+00:00",
  "scheduledPublicationDate": "2026-01-01T00:00:00+00:00",
  "delivery": {
    "url": "string",
    "publishedAt": "2026-01-01T00:00:00+00:00"
  },
  "briefing": {
    "title": "string",
    "language": "string",
    "links": [
      {
        "targetUrl": "string",
        "anchor": "string",
        "type": "dofollow"
      }
    ],
    "extraServices": [
      "list_on_homepage"
    ],
    "additionalInfo": "string",
    "hasProposedDraft": false
  },
  "draft": {
    "authoredByBuyer": false,
    "reviewable": false,
    "hasFile": false
  },
  "stateHistory": [
    {
      "state": "string",
      "date": "2026-01-01T00:00:00+00:00",
      "reason": {
        "code": "other_reasons",
        "additionalInfo": "string"
      }
    }
  ],
  "channel": {
    "created": "api"
  }
}

GET /api/v1/orders/{id}/briefing

Download the draft you attached to an order

The file you attached to the briefing of a post order. 404 when the order has none (briefing.hasProposedDraft is false) or the file cannot be read.

Parameters

Name In Type Description
id Required path string (uuid)

Responses

  • 200

    The file, with its own content type and name.

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X GET "https://tool.growwer.com/api/v1/orders/$ORDER_ID/briefing" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -o briefing

Answers the file itself (application/octet-stream), not JSON.

POST /api/v1/orders/{id}/cancel

Cancel an order

Cancels an order the seller has not accepted yet (state pending_acceptance), as the web does. Its price returns to your wallet's available balance, and the seller is told your reason.

Parameters

Name In Type Description
id Required path string (uuid)
Idempotency-Key Required header string max length 255 A new key for each write, reused only to retry that same write. 1 to 255 printable ASCII characters without spaces (! to ~); any other value answers 400 invalid_idempotency_key.

Request body application/json, Required

Field Type Description
reason Required string min length 10, max length 2000 What the seller is told, 10 to 2000 characters once trimmed.

Responses

  • 200

    The order, cancelled.

  • 400

    The write has no Idempotency-Key header (code idempotency_key_required), the header is not 1 to 255 printable ASCII characters without spaces (code invalid_idempotency_key), or the body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The key is read-only (code insufficient_capability).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 409

    The seller has already accepted or refused the order, or it has finished (code invalid_state), or the Idempotency-Key conflicts (codes idempotency_key_reused, idempotency_request_in_progress, idempotency_outcome_unknown).

  • 422

    The request is not valid (code validation_failed). details.fields holds the messages for each invalid field, by field name.

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X POST "https://tool.growwer.com/api/v1/orders/$ORDER_ID/cancel" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "reason": "example example"
}'

Example response

{
  "id": "00000000-0000-0000-0000-000000000000",
  "reference": "string",
  "type": "post",
  "state": "string",
  "pendingAction": "buyer",
  "price": {
    "amount": 12500,
    "currency": "EUR"
  },
  "project": {
    "id": "00000000-0000-0000-0000-000000000000",
    "url": "string"
  },
  "media": {
    "id": "00000000-0000-0000-0000-000000000000",
    "url": "string"
  },
  "createdAt": "2026-01-01T00:00:00+00:00",
  "updatedAt": "2026-01-01T00:00:00+00:00",
  "scheduledPublicationDate": "2026-01-01T00:00:00+00:00",
  "delivery": {
    "url": "string",
    "publishedAt": "2026-01-01T00:00:00+00:00"
  },
  "briefing": {
    "title": "string",
    "language": "string",
    "links": [
      {
        "targetUrl": "string",
        "anchor": "string",
        "type": "dofollow"
      }
    ],
    "extraServices": [
      "list_on_homepage"
    ],
    "additionalInfo": "string",
    "hasProposedDraft": false
  },
  "draft": {
    "authoredByBuyer": false,
    "reviewable": false,
    "hasFile": false
  },
  "stateHistory": [
    {
      "state": "string",
      "date": "2026-01-01T00:00:00+00:00",
      "reason": {
        "code": "other_reasons",
        "additionalInfo": "string"
      }
    }
  ],
  "channel": {
    "created": "api"
  }
}

GET /api/v1/orders/{id}/draft

Download the draft of an order

The file of the current draft of a post order. 404 when the order has no draft file (draft.hasFile is false) or the file cannot be read.

Parameters

Name In Type Description
id Required path string (uuid)

Responses

  • 200

    The file, with its own content type and name.

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X GET "https://tool.growwer.com/api/v1/orders/$ORDER_ID/draft" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -o draft

Answers the file itself (application/octet-stream), not JSON.

POST /api/v1/orders/{id}/request-draft-update

Request changes to a draft

Asks the seller to change the draft of a post order whose pendingAction is yours to review the draft, as the web does. The seller is told your reason.

Parameters

Name In Type Description
id Required path string (uuid)
Idempotency-Key Required header string max length 255 A new key for each write, reused only to retry that same write. 1 to 255 printable ASCII characters without spaces (! to ~); any other value answers 400 invalid_idempotency_key.

Request body application/json, Required

Field Type Description
reason Required string min length 10, max length 2000 What the seller is told, 10 to 2000 characters once trimmed.

Responses

  • 200

    The order, with its draft back with the seller.

  • 400

    The write has no Idempotency-Key header (code idempotency_key_required), the header is not 1 to 255 printable ASCII characters without spaces (code invalid_idempotency_key), or the body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The key is read-only (code insufficient_capability).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 409

    The order is not a post order with a draft pending your review (code invalid_state), or the Idempotency-Key conflicts (codes idempotency_key_reused, idempotency_request_in_progress, idempotency_outcome_unknown).

  • 422

    The request is not valid (code validation_failed). details.fields holds the messages for each invalid field, by field name.

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X POST "https://tool.growwer.com/api/v1/orders/$ORDER_ID/request-draft-update" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "reason": "example example"
}'

Example response

{
  "id": "00000000-0000-0000-0000-000000000000",
  "reference": "string",
  "type": "post",
  "state": "string",
  "pendingAction": "buyer",
  "price": {
    "amount": 12500,
    "currency": "EUR"
  },
  "project": {
    "id": "00000000-0000-0000-0000-000000000000",
    "url": "string"
  },
  "media": {
    "id": "00000000-0000-0000-0000-000000000000",
    "url": "string"
  },
  "createdAt": "2026-01-01T00:00:00+00:00",
  "updatedAt": "2026-01-01T00:00:00+00:00",
  "scheduledPublicationDate": "2026-01-01T00:00:00+00:00",
  "delivery": {
    "url": "string",
    "publishedAt": "2026-01-01T00:00:00+00:00"
  },
  "briefing": {
    "title": "string",
    "language": "string",
    "links": [
      {
        "targetUrl": "string",
        "anchor": "string",
        "type": "dofollow"
      }
    ],
    "extraServices": [
      "list_on_homepage"
    ],
    "additionalInfo": "string",
    "hasProposedDraft": false
  },
  "draft": {
    "authoredByBuyer": false,
    "reviewable": false,
    "hasFile": false
  },
  "stateHistory": [
    {
      "state": "string",
      "date": "2026-01-01T00:00:00+00:00",
      "reason": {
        "code": "other_reasons",
        "additionalInfo": "string"
      }
    }
  ],
  "channel": {
    "created": "api"
  }
}

POST /api/v1/orders/{id}/request-update

Request changes to a delivery

Asks the seller to change the delivery of an order whose pendingAction is yours to review the delivery, as the web does. The seller is told your reason.

Parameters

Name In Type Description
id Required path string (uuid)
Idempotency-Key Required header string max length 255 A new key for each write, reused only to retry that same write. 1 to 255 printable ASCII characters without spaces (! to ~); any other value answers 400 invalid_idempotency_key.

Request body application/json, Required

Field Type Description
reason Required string min length 10, max length 2000 What the seller is told, 10 to 2000 characters once trimmed.

Responses

  • 200

    The order, back with the seller.

  • 400

    The write has no Idempotency-Key header (code idempotency_key_required), the header is not 1 to 255 printable ASCII characters without spaces (code invalid_idempotency_key), or the body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The key is read-only (code insufficient_capability).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 409

    The order is not pending your review of its delivery (code invalid_state), or the Idempotency-Key conflicts (codes idempotency_key_reused, idempotency_request_in_progress, idempotency_outcome_unknown).

  • 422

    The request is not valid (code validation_failed). details.fields holds the messages for each invalid field, by field name.

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X POST "https://tool.growwer.com/api/v1/orders/$ORDER_ID/request-update" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "reason": "example example"
}'

Example response

{
  "id": "00000000-0000-0000-0000-000000000000",
  "reference": "string",
  "type": "post",
  "state": "string",
  "pendingAction": "buyer",
  "price": {
    "amount": 12500,
    "currency": "EUR"
  },
  "project": {
    "id": "00000000-0000-0000-0000-000000000000",
    "url": "string"
  },
  "media": {
    "id": "00000000-0000-0000-0000-000000000000",
    "url": "string"
  },
  "createdAt": "2026-01-01T00:00:00+00:00",
  "updatedAt": "2026-01-01T00:00:00+00:00",
  "scheduledPublicationDate": "2026-01-01T00:00:00+00:00",
  "delivery": {
    "url": "string",
    "publishedAt": "2026-01-01T00:00:00+00:00"
  },
  "briefing": {
    "title": "string",
    "language": "string",
    "links": [
      {
        "targetUrl": "string",
        "anchor": "string",
        "type": "dofollow"
      }
    ],
    "extraServices": [
      "list_on_homepage"
    ],
    "additionalInfo": "string",
    "hasProposedDraft": false
  },
  "draft": {
    "authoredByBuyer": false,
    "reviewable": false,
    "hasFile": false
  },
  "stateHistory": [
    {
      "state": "string",
      "date": "2026-01-01T00:00:00+00:00",
      "reason": {
        "code": "other_reasons",
        "additionalInfo": "string"
      }
    }
  ],
  "channel": {
    "created": "api"
  }
}

Buyer wallet

Your buyer balance, its movements and documents, and your invoices.

GET /api/v1/invoices

List your invoices

The invoices of your top-ups, newest first.

Parameters

Name In Type Description
limit query integer min 1, max 100, default 20 Invoices per page, 1 to 100.
offset query integer min 0, default 0 Invoices to skip.

Responses

  • 200

    A page of your invoices and how many you have in all.

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 422

    The request is not valid (code validation_failed). details.fields holds the messages for each invalid field, by field name.

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X GET "https://tool.growwer.com/api/v1/invoices" \
  -H "Authorization: Bearer $GROWWER_API_KEY"

Example response

{
  "items": [
    {
      "id": "00000000-0000-0000-0000-000000000000",
      "number": "string",
      "issuedAt": "2026-01-01T00:00:00+00:00",
      "total": {
        "amount": 12500,
        "currency": "EUR"
      }
    }
  ],
  "total": 0
}

GET /api/v1/invoices/{id}/pdf

Download an invoice

The PDF of one of your invoices.

Parameters

Name In Type Description
id Required path string (uuid)

Responses

  • 200

    The invoice.

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X GET "https://tool.growwer.com/api/v1/invoices/$INVOICE_ID/pdf" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -o invoice.pdf

Answers the file itself (application/pdf), not JSON.

GET /api/v1/wallets/buyer

Get your buyer wallet

The balance you buy with, and the page where you top it up.

Responses

  • 200

    Your buyer wallet.

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X GET "https://tool.growwer.com/api/v1/wallets/buyer" \
  -H "Authorization: Bearer $GROWWER_API_KEY"

Example response

{
  "balance": {
    "available": {
      "amount": 12500,
      "currency": "EUR"
    },
    "pending": {
      "amount": 12500,
      "currency": "EUR"
    }
  },
  "topUpUrl": "https://example.com"
}

GET /api/v1/wallets/buyer/transactions

List your buyer wallet movements

Every movement of your buyer wallet, newest first: top-ups, order debits and their refunds, gifts and manual corrections.

Parameters

Name In Type Description
limit query integer min 1, max 100, default 20 Movements per page, 1 to 100.
offset query integer min 0, default 0 Movements to skip.

Responses

  • 200

    A page of your movements and how many there are in all.

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 422

    The request is not valid (code validation_failed). details.fields holds the messages for each invalid field, by field name.

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X GET "https://tool.growwer.com/api/v1/wallets/buyer/transactions" \
  -H "Authorization: Bearer $GROWWER_API_KEY"

Example response

{
  "items": [
    {
      "id": "00000000-0000-0000-0000-000000000000",
      "type": "string",
      "state": "pending_confirmation",
      "direction": "credit",
      "amount": {
        "amount": 12500,
        "currency": "EUR"
      },
      "description": "string",
      "descriptionCode": "string",
      "orderId": "00000000-0000-0000-0000-000000000000",
      "invoiceId": "00000000-0000-0000-0000-000000000000",
      "hasDocument": false,
      "createdAt": "2026-01-01T00:00:00+00:00",
      "updatedAt": "2026-01-01T00:00:00+00:00"
    }
  ],
  "total": 0
}

GET /api/v1/wallets/buyer/transactions/{id}/document

Download the document of a wallet movement

The document Growwer attached to a movement of your buyer wallet. 404 when the movement has none (hasDocument is false) or the file cannot be read.

Parameters

Name In Type Description
id Required path string (uuid)

Responses

  • 200

    The document, with its own content type and name.

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X GET "https://tool.growwer.com/api/v1/wallets/buyer/transactions/$TRANSACTION_ID/document" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -o document

Answers the file itself (application/octet-stream), not JSON.

Managed services

Your managed service campaigns, their proposals, and requests to Growwer.

POST /api/v1/managed-service/campaign-requests

Request a new campaign

Asks your account manager for a new campaign, as the managed service page does: the team emails you to agree on it. For accounts with a managed service (GET /me managedService.active); without one, POST /managed-service/info-requests asks about the service.

The API admits a limited number of such requests per user and UTC day (5 by default; the refusal message states the current limit). Past it, it answers 429 managed_service_request_limit_reached, with Retry-After the time to the next UTC midnight. A request that is refused (4xx) does not count; one that fails on the server (5xx) does, as it may have been sent.

Parameters

Name In Type Description
Idempotency-Key Required header string max length 255 A new key for each write, reused only to retry that same write. 1 to 255 printable ASCII characters without spaces (! to ~); any other value answers 400 invalid_idempotency_key.

Request body application/json, Required

Field Type Description
projectUrl Required string The website the campaign is for.
goal Required link_building | branding | reputation | ai_visibility
country Required string ISO 3166-1 alpha-2 code of the target country.
duration Required one_time | monthly
estimatedBudget Required Money At least 500 EUR. Only EUR is accepted.
additionalInfo string | null min length 10, max length 2000

Responses

  • 202

    The request was sent.

  • 400

    The write has no Idempotency-Key header (code idempotency_key_required), the header is not 1 to 255 printable ASCII characters without spaces (code invalid_idempotency_key), or the body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The key is read-only (code insufficient_capability).

  • 409

    You have no managed service yet (code managed_service_required), or the Idempotency-Key conflicts (codes idempotency_key_reused, idempotency_request_in_progress, idempotency_outcome_unknown).

  • 422

    The request is not valid (code validation_failed). details.fields holds the messages for each invalid field, by field name.

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X POST "https://tool.growwer.com/api/v1/managed-service/campaign-requests" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "projectUrl": "string",
  "goal": "link_building",
  "country": "string",
  "duration": "one_time",
  "estimatedBudget": {
    "amount": 12500,
    "currency": "EUR"
  },
  "additionalInfo": "example example"
}'

Example response

{
  "status": "sent"
}

GET /api/v1/managed-service/campaigns

List your managed service campaigns

The campaigns your managed service runs for you, newest first. Without a managed service the list is empty: GET /me says whether you have one.

Parameters

Name In Type Description
limit query integer min 1, max 100, default 20 Campaigns per page, 1 to 100.
offset query integer min 0, default 0 Campaigns to skip.

Responses

  • 200

    A page of your campaigns and how many you have in all.

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 422

    The request is not valid (code validation_failed). details.fields holds the messages for each invalid field, by field name.

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X GET "https://tool.growwer.com/api/v1/managed-service/campaigns" \
  -H "Authorization: Bearer $GROWWER_API_KEY"

Example response

{
  "items": [
    {
      "id": "00000000-0000-0000-0000-000000000000",
      "name": "string",
      "state": "pending_proposal_delivery",
      "deadline": "2026-01-01T00:00:00+00:00",
      "budget": {
        "amount": 12500,
        "currency": "EUR"
      },
      "project": {
        "id": "00000000-0000-0000-0000-000000000000",
        "url": "string"
      },
      "strategy": {
        "goal": "link_building",
        "countries": [],
        "languages": [],
        "topics": [],
        "definition": "string"
      },
      "proposalCounts": {
        "proposed": 0,
        "accepted": 0,
        "cancelled": 0,
        "purchase_started": 0,
        "purchase_finished": 0
      },
      "proposalsTotal": {
        "amount": 12500,
        "currency": "EUR"
      },
      "budgetExceededBy": {
        "amount": 12500,
        "currency": "EUR"
      },
      "createdAt": "2026-01-01T00:00:00+00:00",
      "updatedAt": "2026-01-01T00:00:00+00:00"
    }
  ],
  "total": 0
}

GET /api/v1/managed-service/campaigns/{id}

Get a managed service campaign

Parameters

Name In Type Description
id Required path string (uuid)

Responses

  • 200

    The campaign.

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X GET "https://tool.growwer.com/api/v1/managed-service/campaigns/$CAMPAIGN_ID" \
  -H "Authorization: Bearer $GROWWER_API_KEY"

Example response

{
  "id": "00000000-0000-0000-0000-000000000000",
  "name": "string",
  "state": "pending_proposal_delivery",
  "deadline": "2026-01-01T00:00:00+00:00",
  "budget": {
    "amount": 12500,
    "currency": "EUR"
  },
  "project": {
    "id": "00000000-0000-0000-0000-000000000000",
    "url": "string"
  },
  "strategy": {
    "goal": "link_building",
    "countries": [
      "string"
    ],
    "languages": [
      "string"
    ],
    "topics": [
      0
    ],
    "definition": "string"
  },
  "proposalCounts": {
    "proposed": 0,
    "accepted": 0,
    "cancelled": 0,
    "purchase_started": 0,
    "purchase_finished": 0
  },
  "proposalsTotal": {
    "amount": 12500,
    "currency": "EUR"
  },
  "budgetExceededBy": {
    "amount": 12500,
    "currency": "EUR"
  },
  "createdAt": "2026-01-01T00:00:00+00:00",
  "updatedAt": "2026-01-01T00:00:00+00:00"
}

GET /api/v1/managed-service/campaigns/{id}/proposals

List the proposals of a campaign

The medias the team proposes for the campaign, newest first. Those proposed await your review (POST /managed-service/campaigns/{id}/proposals/review).

Parameters

Name In Type Description
id Required path string (uuid)
state query string Only proposals in these states, comma-separated: proposed, accepted, cancelled, purchase_started, purchase_finished.
limit query integer min 1, max 100, default 20 Proposals per page, 1 to 100.
offset query integer min 0, default 0 Proposals to skip.

Responses

  • 200

    A page of the campaign's proposals and how many match in all.

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 422

    The request is not valid (code validation_failed). details.fields holds the messages for each invalid field, by field name.

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X GET "https://tool.growwer.com/api/v1/managed-service/campaigns/$CAMPAIGN_ID/proposals" \
  -H "Authorization: Bearer $GROWWER_API_KEY"

Example response

{
  "items": [
    {
      "id": "00000000-0000-0000-0000-000000000000",
      "state": "proposed",
      "media": {
        "id": "00000000-0000-0000-0000-000000000000",
        "domain": "string",
        "language": "string",
        "country": "string"
      },
      "service": "post",
      "proposedPrice": {
        "amount": 12500,
        "currency": "EUR"
      },
      "currentPrice": {
        "amount": 12500,
        "currency": "EUR"
      },
      "briefing": {
        "mainInfo": "string",
        "links": [],
        "additionalInfo": "string",
        "requiresDraft": false
      },
      "stateReason": "string",
      "orderId": "00000000-0000-0000-0000-000000000000",
      "orderState": "string",
      "createdAt": "2026-01-01T00:00:00+00:00",
      "updatedAt": "2026-01-01T00:00:00+00:00"
    }
  ],
  "total": 0
}

GET /api/v1/managed-service/campaigns/{id}/proposals-summary

Download the summary of a campaign's proposals

The PDF the campaign page offers: the proposals awaiting your review, accepted or being published, newest first, in your language. With extended=true it also shows each proposal's price, the price it was proposed at (proposedPrice). One summary of yours is rendered at a time: a request while another is being rendered gets 429 proposals_summary_in_progress, to retry after the Retry-After header.

Parameters

Name In Type Description
id Required path string (uuid)
extended query true | false default "false" Whether to show the prices.

Responses

  • 200

    The summary.

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 422

    The request is not valid (code validation_failed). details.fields holds the messages for each invalid field, by field name.

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X GET "https://tool.growwer.com/api/v1/managed-service/campaigns/$CAMPAIGN_ID/proposals-summary" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -o proposals-summary.pdf

Answers the file itself (application/pdf), not JSON.

POST /api/v1/managed-service/campaigns/{id}/proposals/estimate

Estimate accepting proposals

Totals the campaign's accepted proposals that still await their order and the proposed ones you name in accept, and compares the total with your wallet's available balance. Each proposal counts at its currentPrice, or at its proposedPrice when the media no longer offers the service. Nothing is accepted, charged or reserved, so a read-only key may call it and it takes no Idempotency-Key.

The estimate is advisory: accepting a proposal charges nothing. The team charges your wallet when it places the order for an accepted proposal, and the same balance pays for the orders you place yourself.

Parameters

Name In Type Description
id Required path string (uuid)

Request body application/json, Optional

Field Type Description
accept string (uuid)[] The proposed proposals you would accept, each once. Leave it out to price only the accepted ones.

Responses

  • 200

    The totals and how your wallet covers them.

  • 400

    The body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 404

    The campaign is not yours, or a proposal named in accept is not one of the campaign's (code not_found, with details.proposalId).

  • 409

    A proposal named in accept is not awaiting your review (code invalid_state, with details.proposalIds).

  • 422

    The request is not valid (code validation_failed). details.fields holds the messages for each invalid field, by field name.

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X POST "https://tool.growwer.com/api/v1/managed-service/campaigns/$CAMPAIGN_ID/proposals/estimate" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "accept": [
    "00000000-0000-0000-0000-000000000000"
  ]
}'

Example response

{
  "acceptedTotal": {
    "amount": 12500,
    "currency": "EUR"
  },
  "selectedTotal": {
    "amount": 12500,
    "currency": "EUR"
  },
  "total": {
    "amount": 12500,
    "currency": "EUR"
  },
  "availableBalance": {
    "amount": 12500,
    "currency": "EUR"
  },
  "missing": {
    "amount": 12500,
    "currency": "EUR"
  },
  "sufficientBalance": false
}

POST /api/v1/managed-service/campaigns/{id}/proposals/review

Accept or reject proposals

Decides on every proposal of the campaign that awaits your review (state proposed), as the campaign page does: name each one once, with accept or reject, and a comment for the team to reject one. Their order in the body does not matter. Accepting charges nothing: the team places the orders for the accepted proposals and your wallet pays for them then. POST /managed-service/campaigns/{id}/proposals/estimate says what they would cost.

When every proposal is accepted the campaign moves to proposal_accepted; when one is rejected the team is told and prepares others. A proposal someone else moved on the campaign page while the request ran is answered in the state it reached.

Parameters

Name In Type Description
id Required path string (uuid)
Idempotency-Key Required header string max length 255 A new key for each write, reused only to retry that same write. 1 to 255 printable ASCII characters without spaces (! to ~); any other value answers 400 invalid_idempotency_key.

Request body application/json, Required

Field Type Description
reviews Required object[] min items 1 One decision on each proposal of the campaign that is proposed, each named once. Their order does not matter.

Responses

  • 200

    The campaign and the proposals you reviewed, as they now are.

  • 400

    The write has no Idempotency-Key header (code idempotency_key_required), the header is not 1 to 255 printable ASCII characters without spaces (code invalid_idempotency_key), or the body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The key is read-only (code insufficient_capability).

  • 404

    The campaign is not yours, or a proposal named is not one of the campaign's (code not_found, with details.proposalId).

  • 409

    No proposal of the campaign awaits your review (code no_pending_proposals), or a proposal named does not (code invalid_state, with details.proposalIds), or the Idempotency-Key conflicts (codes idempotency_key_reused, idempotency_request_in_progress, idempotency_outcome_unknown).

  • 422

    The body is invalid (code validation_failed), or it leaves out proposals awaiting your review (code review_incomplete, with details.missingProposalIds).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X POST "https://tool.growwer.com/api/v1/managed-service/campaigns/$CAMPAIGN_ID/proposals/review" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "reviews": [
    {
      "proposalId": "00000000-0000-0000-0000-000000000000",
      "decision": "accept",
      "comment": "string"
    }
  ]
}'

POST /api/v1/managed-service/info-requests

Ask about the managed service

Asks the team about the managed service for your website, as the managed service landing page does: the team contacts you. For accounts without a managed service (GET /me managedService.active); with one, POST /managed-service/campaign-requests requests a new campaign.

The API admits a limited number of such requests per user and UTC day (5 by default; the refusal message states the current limit). Past it, it answers 429 managed_service_request_limit_reached, with Retry-After the time to the next UTC midnight. A request that is refused (4xx) does not count; one that fails on the server (5xx) does, as it may have been sent.

Parameters

Name In Type Description
Idempotency-Key Required header string max length 255 A new key for each write, reused only to retry that same write. 1 to 255 printable ASCII characters without spaces (! to ~); any other value answers 400 invalid_idempotency_key.

Request body application/json, Required

Field Type Description
projectUrl Required string The website the campaign is for.
goal Required link_building | branding | reputation | ai_visibility
country Required string ISO 3166-1 alpha-2 code of the target country.
duration Required one_time | monthly
estimatedBudget Required Money At least 500 EUR. Only EUR is accepted.
additionalInfo string | null min length 10, max length 2000

Responses

  • 202

    The request was sent.

  • 400

    The write has no Idempotency-Key header (code idempotency_key_required), the header is not 1 to 255 printable ASCII characters without spaces (code invalid_idempotency_key), or the body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The key is read-only (code insufficient_capability).

  • 409

    You already have a managed service (code managed_service_exists), or the Idempotency-Key conflicts (codes idempotency_key_reused, idempotency_request_in_progress, idempotency_outcome_unknown).

  • 422

    The request is not valid (code validation_failed). details.fields holds the messages for each invalid field, by field name.

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X POST "https://tool.growwer.com/api/v1/managed-service/info-requests" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "projectUrl": "string",
  "goal": "link_building",
  "country": "string",
  "duration": "one_time",
  "estimatedBudget": {
    "amount": 12500,
    "currency": "EUR"
  },
  "additionalInfo": "example example"
}'

Example response

{
  "status": "sent"
}

Seller

Seller account

Your seller holiday, during which your medias take no orders.

PUT /api/v1/seller/holiday

Set your holiday

Starts, changes or ends your holiday, as the holiday settings of your profile page do. While you are on holiday your medias take no orders. endDate is the day your holiday ends, at 00:00 of that day in Growwer's server time, and must be later than now; leave it out, or send null, for a holiday you end yourself. Ending the holiday takes no endDate.

A holiday that already is as you ask is answered 200 without a change. A restricted seller account stays on holiday until the restriction is lifted, so any change is refused. Your holiday is also in GET /me.

Parameters

Name In Type Description
Idempotency-Key Required header string max length 255 A new key for each write, reused only to retry that same write. 1 to 255 printable ASCII characters without spaces (! to ~); any other value answers 400 invalid_idempotency_key.

Request body application/json, Required

Field Type Description
onHoliday Required boolean
endDate string (date) | null

Responses

  • 200

    Your holiday, as it now is.

  • 400

    The write has no Idempotency-Key header (code idempotency_key_required), the header is not 1 to 255 printable ASCII characters without spaces (code invalid_idempotency_key), or the body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The key is read-only (code insufficient_capability).

  • 409

    Your seller account is restricted, so its holiday cannot change (code seller_restricted), or the Idempotency-Key conflicts (codes idempotency_key_reused, idempotency_request_in_progress, idempotency_outcome_unknown).

  • 422

    The body is not valid (code validation_failed): an endDate that is not a later day, or one sent with onHoliday false.

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X PUT "https://tool.growwer.com/api/v1/seller/holiday" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "onHoliday": false,
  "endDate": "2026-12-01"
}'

Example response

{
  "onHoliday": false,
  "endDate": "2026-01-01"
}

Seller medias

The medias you sell on and their fees.

GET /api/v1/seller/medias

List the medias you sell on

Your medias in every state, with your fees and how they perform, newest first by default.

Parameters

Name In Type Description
state query string Only the medias in one of these states, comma-separated: analyzing, pending_review, active, deactivated. An unknown state is refused with 422.
sort query -createdAt | createdAt | -numPublishedOrders | numPublishedOrders | -publicationRate | publicationRate default "-createdAt" By creation date, number of published orders or publication rate; a leading - sorts descending. Medias with no publication rate come first ascending, last descending. Ties come in id order.
limit query integer min 1, max 100, default 20 Medias per page, 1 to 100.
offset query integer min 0, default 0 Medias to skip.

Responses

  • 200

    A page of your medias and how many match in all.

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 422

    The request is not valid (code validation_failed). details.fields holds the messages for each invalid field, by field name.

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X GET "https://tool.growwer.com/api/v1/seller/medias" \
  -H "Authorization: Bearer $GROWWER_API_KEY"

Example response

{
  "items": [
    {
      "id": "00000000-0000-0000-0000-000000000000",
      "domain": "example.com",
      "url": "string",
      "typology": "blog",
      "state": "analyzing",
      "deactivationStrikes": 0,
      "country": "string",
      "language": "string",
      "topics": [
        0
      ],
      "bannedTopics": [
        0
      ],
      "providerComments": "string",
      "publicationRate": 0,
      "averageDaysToPublication": 0,
      "numPublishedOrders": 0,
      "onHoliday": false,
      "flashOffer": {
        "discountPercent": 0,
        "endsAt": "2026-01-01T00:00:00+00:00"
      },
      "postOffer": {
        "pricingOptions": [],
        "maximumLinks": 0,
        "advertisingLabellingPolicy": "always",
        "promotedSocialNetworks": [
          "twitter"
        ],
        "extraServices": []
      },
      "linkAdditionOffer": {
        "pricingOptions": []
      },
      "competitiveFees": {
        "feeWhenSellerWritesPost": {
          "amount": null,
          "currency": null
        },
        "feeWhenSellerDoesNotWritePost": {
          "amount": null,
          "currency": null
        }
      },
      "createdAt": "2026-01-01T00:00:00+00:00"
    }
  ],
  "total": 0
}

POST /api/v1/seller/medias

Register a media you sell on

Registers one of your medias, as the media create page does. The media is created with state: pending_review and Growwer reviews it in the background, as for a media registered on the web; GET /seller/medias/{id} shows its state.

  • rootUrl is the media's root URL; typology is blog or press.
  • country and language take the codes GET /vocabulary publishes; topics and bannedTopics take its topic ids, at most 100 each, and no topic may be both; bannedTopics left out bans none.
  • postOffer.pricingOptions lists every link type you offer for sponsored posts, at least one. Each option needs at least one fee, and every option must offer the same sides (written by you, written by the buyer or Growwer), as the page requires. Each fee is at least 1 EUR, sent in cents.
  • postOffer.maximumLinks is how many links a sponsored post may hold, 1 to 4; postOffer.advertisingLabellingPolicy whether posts are labelled as advertising, never when left out; postOffer.promotedSocialNetworks the social networks posts are promoted on, none when left out.
  • linkAdditionOffer.pricingOptions lists the link types you offer for adding a link to an existing article; left out, null or an empty list offers none.
  • extraServices lists the extra services you offer, none when left out. It is sent at the top level, although GET /seller/medias/{id} reads it inside postOffer.
  • acceptMediaPublishingTerms and declareMediaOwnership must be true: they accept the media publishing terms and declare that you own or manage the media, as the page's checkboxes do, and are recorded as your consents for the media.

A media registered already, by you or by anyone else, is refused with 409 media_already_registered, and each such refusal counts toward a daily limit of refusals (429 media_duplicate_limit_reached once reached); medias not registered yet are never limited by it.

To register many medias, call this operation once per media, each call with its own Idempotency-Key and its own answer (201, 409, 422 or 429); there is no batch operation. The request limits apply to the loop as to any calls (600 requests per minute per user and 300 per key), and each 409 media_already_registered counts toward the daily limit of refusals as it does for a single call. Retry a call that got no answer with its same Idempotency-Key, as for any write.

Parameters

Name In Type Description
Idempotency-Key Required header string max length 255 A new key for each write, reused only to retry that same write. 1 to 255 printable ASCII characters without spaces (! to ~); any other value answers 400 invalid_idempotency_key.

Request body application/json, Required

Field Type Description
rootUrl Required string (uri)
typology Required blog | press
country Required string A country code from GET /vocabulary.
language Required string A language code from GET /vocabulary.
topics Required integer[] min items 1, max items 100 Topic ids from GET /vocabulary.
bannedTopics integer[] max items 100 The topic ids from GET /vocabulary the media refuses.
postOffer Required object
linkAdditionOffer object | null
extraServices object[]
acceptMediaPublishingTerms Required true Must be true: you accept the media publishing terms.
declareMediaOwnership Required true Must be true: you own or manage this media.

Responses

  • 201

    The media, registered with state: pending_review. Location is its URL.

  • 400

    The write has no Idempotency-Key header (code idempotency_key_required), the header is not 1 to 255 printable ASCII characters without spaces (code invalid_idempotency_key), or the body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The key is read-only (code insufficient_capability).

  • 409

    Nothing was registered: your seller account is restricted (code seller_restricted); or the media is registered already (code media_already_registered), with details.mediaId naming it only when it is yours, and the refusal counts toward the daily limit of such refusals; or the Idempotency-Key conflicts (codes idempotency_key_reused, idempotency_request_in_progress, idempotency_outcome_unknown).

  • 422

    The body is not valid (code validation_failed): details.fields names each field by its path in the body, such as postOffer.pricingOptions.0.feeWhenSellerWritesPost or topics.1, with the rule the media create page applies. Nothing was registered, and the refusal does not count toward the daily limit of media_already_registered refusals.

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X POST "https://tool.growwer.com/api/v1/seller/medias" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "rootUrl": "https://www.example.com",
  "typology": "blog",
  "country": "ES",
  "language": "es",
  "topics": [
    3,
    7
  ],
  "bannedTopics": [
    12
  ],
  "postOffer": {
    "pricingOptions": [
      {
        "linkType": "dofollow",
        "feeWhenSellerWritesPost": {
          "amount": 12000,
          "currency": "EUR"
        },
        "feeWhenSellerDoesNotWritePost": {
          "amount": 10000,
          "currency": "EUR"
        }
      }
    ],
    "maximumLinks": 2,
    "advertisingLabellingPolicy": "always",
    "promotedSocialNetworks": [
      "twitter"
    ]
  },
  "linkAdditionOffer": {
    "pricingOptions": [
      {
        "linkType": "dofollow",
        "fee": {
          "amount": 6000,
          "currency": "EUR"
        }
      }
    ]
  },
  "extraServices": [
    {
      "name": "list_on_homepage",
      "cost": {
        "amount": 5000,
        "currency": "EUR"
      }
    }
  ],
  "acceptMediaPublishingTerms": true,
  "declareMediaOwnership": true
}'

GET /api/v1/seller/medias/{id}

Get a media you sell on

One of your medias, with your fees, its extras, the competitive fees Growwer suggests and how it performs. Another seller's media is not found.

Parameters

Name In Type Description
id Required path string (uuid)

Responses

  • 200

    The media.

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X GET "https://tool.growwer.com/api/v1/seller/medias/$MEDIA_ID" \
  -H "Authorization: Bearer $GROWWER_API_KEY"

PATCH /api/v1/seller/medias/{id}

Edit the offer of a media you sell on

Sets the fees, extra services, typology and post offer characteristics of one of your medias, as the media edit page saves them. Send only the fields you change: a field you send replaces that field wholly, a field you leave out stays as it is, inside postOffer too (sending only postOffer.maximumLinks keeps your fees).

  • typology is blog or press.
  • postOffer.pricingOptions lists every link type you offer for sponsored posts, at least one. A link type you leave out is no longer offered. Each option needs at least one fee, and every option must offer the same sides (written by you, written by the buyer or Growwer), as the page requires. Each fee is at least 1 EUR.
  • postOffer.maximumLinks is how many links a sponsored post may hold, 1 to 4; postOffer.advertisingLabellingPolicy whether posts are labelled as advertising (always or never); postOffer.promotedSocialNetworks the full list of social networks posts are promoted on, each once (an empty list for none).
  • linkAdditionOffer.pricingOptions lists the link types you offer for adding a link to an existing article; null or an empty list stops offering link additions.
  • extraServices is the full list of extra services you offer; an empty list removes them all. It is sent at the top level, although GET /seller/medias/{id} reads it inside postOffer.

The fees are your base fees, the ones GET /seller/medias/{id} returns: while a flash offer runs, its discount keeps applying on top of them, and currentCost shows the result. currentCost may be sent back as read and is ignored. Country, language, topics and comments are not edited here.

As on the page, a change of the post offer's price may cancel the managed-service proposals accepted for the media whose price it changes, and raising the price above the best competitor's tells Growwer's support team. A restricted seller account may edit its offer.

Parameters

Name In Type Description
id Required path string (uuid)
Idempotency-Key Required header string max length 255 A new key for each write, reused only to retry that same write. 1 to 255 printable ASCII characters without spaces (! to ~); any other value answers 400 invalid_idempotency_key.

Request body application/json, Required

Field Type Description
typology blog | press
postOffer object
linkAdditionOffer object | null
extraServices object[]

Responses

  • 200

    The media, with the changes saved.

  • 400

    The write has no Idempotency-Key header (code idempotency_key_required), the header is not 1 to 255 printable ASCII characters without spaces (code invalid_idempotency_key), or the body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The key is read-only (code insufficient_capability).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 409

    The Idempotency-Key conflicts (codes idempotency_key_reused, idempotency_request_in_progress, idempotency_outcome_unknown).

  • 422

    The body is not valid (code validation_failed): details.fields names each field by its path in the body, such as postOffer.pricingOptions.0.feeWhenSellerWritesPost, with the rule the media edit page applies. Nothing changed.

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X PATCH "https://tool.growwer.com/api/v1/seller/medias/$MEDIA_ID" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "typology": "blog",
  "postOffer": {
    "maximumLinks": 2,
    "advertisingLabellingPolicy": "always",
    "promotedSocialNetworks": [
      "twitter",
      "newsletter"
    ],
    "pricingOptions": [
      {
        "linkType": "dofollow",
        "feeWhenSellerWritesPost": {
          "amount": 12000,
          "currency": "EUR"
        },
        "feeWhenSellerDoesNotWritePost": {
          "amount": 10000,
          "currency": "EUR"
        }
      },
      {
        "linkType": "nofollow",
        "feeWhenSellerWritesPost": {
          "amount": 9000,
          "currency": "EUR"
        },
        "feeWhenSellerDoesNotWritePost": {
          "amount": 7000,
          "currency": "EUR"
        }
      }
    ]
  },
  "linkAdditionOffer": null,
  "extraServices": [
    {
      "name": "list_on_homepage",
      "cost": {
        "amount": 5000,
        "currency": "EUR"
      }
    }
  ]
}'

DELETE /api/v1/seller/medias/{id}

Delete a media you sell on

Deletes one of your medias, in any state, as the media delete button does. The media must have no order in progress: close or reject its orders first. The API never cancels an order for you, and an order placed while the delete runs is refused, never cancelled.

What goes with the media, as on the web:

  • A running flash offer ends with it, and its managed-service proposals are deleted.
  • Its finished orders stay, with the media's URL; they read media.id null.
  • Buyers' favourites and discards of the domain stay, as they are kept by domain.
  • The catalogue search drops the media shortly after the 204, as the search index updates asynchronously.

Repeating the call with the same Idempotency-Key answers the same 204 with Idempotent-Replayed: true; a new key after the delete answers 404.

Parameters

Name In Type Description
id Required path string (uuid)
Idempotency-Key Required header string max length 255 A new key for each write, reused only to retry that same write. 1 to 255 printable ASCII characters without spaces (! to ~); any other value answers 400 invalid_idempotency_key.

Responses

  • 204

    The media was deleted.

  • 400

    The write has no Idempotency-Key header (code idempotency_key_required), the header is not 1 to 255 printable ASCII characters without spaces (code invalid_idempotency_key), or the body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The key is read-only (code insufficient_capability).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 409

    Nothing was deleted. Your seller account is restricted (code seller_restricted); the media has orders that are not finished (code media_has_active_orders, details.orderIds lists them: close or reject them first); or the Idempotency-Key conflicts (codes idempotency_key_reused, idempotency_request_in_progress, idempotency_outcome_unknown).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X DELETE "https://tool.growwer.com/api/v1/seller/medias/$MEDIA_ID" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"

POST /api/v1/seller/medias/{id}/apply-competitive-fees

Set the competitive fees Growwer suggests

Sets your post fees for the media to the competitive fees Growwer suggests, the competitiveFees of GET /seller/medias/{id}, as the button on the media edit page does. Each link type you offer gets the suggested fee on each side it already has a fee for; no link type gains a side it did not offer.

Two differences from the page: a side Growwer has no suggestion for keeps your fee (the page clears it), and the fixed fee the page falls back to when it cannot find a competitive one is never applied (the page applies it). When neither side has a suggestion, nothing changes and the answer is 409 no_competitive_fees. When your fees already are the suggested ones, or the suggestion is only for a side none of your link types offers, nothing changes and the answer is 200.

The fees set are your base fees: a running flash offer keeps discounting them. As with a fee edit, a change of the price may cancel the managed-service proposals accepted for the media whose price it changes. Send no body, or an empty object.

Parameters

Name In Type Description
id Required path string (uuid)
Idempotency-Key Required header string max length 255 A new key for each write, reused only to retry that same write. 1 to 255 printable ASCII characters without spaces (! to ~); any other value answers 400 invalid_idempotency_key.

Responses

  • 200

    The media, with the suggested fees set.

  • 400

    The write has no Idempotency-Key header (code idempotency_key_required), the header is not 1 to 255 printable ASCII characters without spaces (code invalid_idempotency_key), or the body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The key is read-only (code insufficient_capability).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 409

    Growwer has no competitive fees to suggest for the media now (code no_competitive_fees), or the Idempotency-Key conflicts (codes idempotency_key_reused, idempotency_request_in_progress, idempotency_outcome_unknown).

  • 422

    The body is not empty, or the fees set break a rule of the media edit page (code validation_failed). Nothing changed.

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X POST "https://tool.growwer.com/api/v1/seller/medias/$MEDIA_ID/apply-competitive-fees" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"

Seller orders

The orders placed on your medias, and accepting, delivering or refusing them.

GET /api/v1/seller/orders

List the orders you sell

The orders placed on your medias, newest first by default. To follow them, poll with sort=updatedAt and updatedSince set to the updatedAt of the last order you saw: an order moves to the end of that list each time it changes state. The orders you bought are under GET /orders.

Parameters

Name In Type Description
mediaId query string (uuid) Only the orders on this media of yours.
state query string Only the orders in one of these states, comma-separated. An unknown state is refused with 422 and the list of valid ones.
updatedSince query string (date-time) Only the orders whose updatedAt is at or after this instant, an ISO-8601 date-time with its offset (send + as %2B).
sort query -createdAt | updatedAt default "-createdAt" -createdAt, newest first, or updatedAt, least recently changed first. Orders of the same instant come in id order.
limit query integer min 1, max 100, default 20 Orders per page, 1 to 100.
offset query integer min 0, default 0 Orders to skip.

Responses

  • 200

    A page of the orders you sell and how many match in all.

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 422

    The request is not valid (code validation_failed). details.fields holds the messages for each invalid field, by field name.

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X GET "https://tool.growwer.com/api/v1/seller/orders" \
  -H "Authorization: Bearer $GROWWER_API_KEY"

Example response

{
  "items": [
    {
      "id": "00000000-0000-0000-0000-000000000000",
      "reference": "string",
      "type": "post",
      "state": "string",
      "requiresYourAction": false,
      "availableActions": [
        "accept"
      ],
      "earnings": {
        "amount": 12500,
        "currency": "EUR"
      },
      "project": {
        "url": "string"
      },
      "media": {
        "id": "00000000-0000-0000-0000-000000000000",
        "url": "string"
      },
      "createdAt": "2026-01-01T00:00:00+00:00",
      "updatedAt": "2026-01-01T00:00:00+00:00",
      "scheduledPublicationDate": "2026-01-01T00:00:00+00:00",
      "delivery": {
        "url": "string",
        "publishedAt": "2026-01-01T00:00:00+00:00"
      },
      "briefing": {
        "title": "string",
        "language": "string",
        "links": [],
        "extraServices": [
          "list_on_homepage"
        ],
        "additionalInfo": "string",
        "writtenBy": "seller",
        "hasProposedDraft": false
      },
      "draft": {
        "reviewable": false,
        "hasFile": false
      },
      "stateHistory": [
        {
          "state": null,
          "date": null,
          "reason": null
        }
      ],
      "channel": {
        "created": "api",
        "accepted": "api"
      }
    }
  ],
  "total": 0
}

GET /api/v1/seller/orders/{id}

Get an order you sell

One order placed on one of your medias, with its briefing, its draft and its state history. An order you only bought is not found here; it is under GET /orders/{id}.

Parameters

Name In Type Description
id Required path string (uuid)

Responses

  • 200

    The order.

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X GET "https://tool.growwer.com/api/v1/seller/orders/$ORDER_ID" \
  -H "Authorization: Bearer $GROWWER_API_KEY"

Example response

{
  "id": "00000000-0000-0000-0000-000000000000",
  "reference": "string",
  "type": "post",
  "state": "string",
  "requiresYourAction": false,
  "availableActions": [
    "accept"
  ],
  "earnings": {
    "amount": 12500,
    "currency": "EUR"
  },
  "project": {
    "url": "string"
  },
  "media": {
    "id": "00000000-0000-0000-0000-000000000000",
    "url": "string"
  },
  "createdAt": "2026-01-01T00:00:00+00:00",
  "updatedAt": "2026-01-01T00:00:00+00:00",
  "scheduledPublicationDate": "2026-01-01T00:00:00+00:00",
  "delivery": {
    "url": "string",
    "publishedAt": "2026-01-01T00:00:00+00:00"
  },
  "briefing": {
    "title": "string",
    "language": "string",
    "links": [
      {
        "targetUrl": "string",
        "anchor": "string",
        "type": "dofollow"
      }
    ],
    "extraServices": [
      "list_on_homepage"
    ],
    "additionalInfo": "string",
    "writtenBy": "seller",
    "hasProposedDraft": false
  },
  "draft": {
    "reviewable": false,
    "hasFile": false
  },
  "stateHistory": [
    {
      "state": "string",
      "date": "2026-01-01T00:00:00+00:00",
      "reason": {
        "code": "other_reasons",
        "additionalInfo": "string"
      }
    }
  ],
  "channel": {
    "created": "api",
    "accepted": "api"
  }
}

POST /api/v1/seller/orders/{id}/accept

Accept an order

Accepts an order placed on one of your medias (state pending_acceptance), as the web does. A post order whose draft is still to be written (by you or by Growwer) moves to pending_draft; any other order moves to pending_order_delivery. The buyer is told. Takes no body.

Parameters

Name In Type Description
id Required path string (uuid)
Idempotency-Key Required header string max length 255 A new key for each write, reused only to retry that same write. 1 to 255 printable ASCII characters without spaces (! to ~); any other value answers 400 invalid_idempotency_key.

Responses

  • 200

    The order, accepted.

  • 400

    The write has no Idempotency-Key header (code idempotency_key_required), the header is not 1 to 255 printable ASCII characters without spaces (code invalid_idempotency_key), or the body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The key is read-only (code insufficient_capability).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 409

    The order is not waiting for your acceptance (code invalid_state), your seller account is restricted and accepts no orders (code seller_restricted; support can tell you why), or the Idempotency-Key conflicts (codes idempotency_key_reused, idempotency_request_in_progress, idempotency_outcome_unknown).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X POST "https://tool.growwer.com/api/v1/seller/orders/$ORDER_ID/accept" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"

Example response

{
  "id": "00000000-0000-0000-0000-000000000000",
  "reference": "string",
  "type": "post",
  "state": "string",
  "requiresYourAction": false,
  "availableActions": [
    "accept"
  ],
  "earnings": {
    "amount": 12500,
    "currency": "EUR"
  },
  "project": {
    "url": "string"
  },
  "media": {
    "id": "00000000-0000-0000-0000-000000000000",
    "url": "string"
  },
  "createdAt": "2026-01-01T00:00:00+00:00",
  "updatedAt": "2026-01-01T00:00:00+00:00",
  "scheduledPublicationDate": "2026-01-01T00:00:00+00:00",
  "delivery": {
    "url": "string",
    "publishedAt": "2026-01-01T00:00:00+00:00"
  },
  "briefing": {
    "title": "string",
    "language": "string",
    "links": [
      {
        "targetUrl": "string",
        "anchor": "string",
        "type": "dofollow"
      }
    ],
    "extraServices": [
      "list_on_homepage"
    ],
    "additionalInfo": "string",
    "writtenBy": "seller",
    "hasProposedDraft": false
  },
  "draft": {
    "reviewable": false,
    "hasFile": false
  },
  "stateHistory": [
    {
      "state": "string",
      "date": "2026-01-01T00:00:00+00:00",
      "reason": {
        "code": "other_reasons",
        "additionalInfo": "string"
      }
    }
  ],
  "channel": {
    "created": "api",
    "accepted": "api"
  }
}

GET /api/v1/seller/orders/{id}/briefing

Download the draft the buyer attached to an order

The file the buyer attached to the briefing of a post order you sell. 404 when the order has none (briefing.hasProposedDraft is false) or the file cannot be read.

Parameters

Name In Type Description
id Required path string (uuid)

Responses

  • 200

    The file, with its own content type and name.

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X GET "https://tool.growwer.com/api/v1/seller/orders/$ORDER_ID/briefing" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -o briefing

Answers the file itself (application/octet-stream), not JSON.

POST /api/v1/seller/orders/{id}/deliver

Deliver an order

Delivers an order placed on one of your medias (state pending_order_delivery), as the web does. A post order takes the URL of the published post and the confirmation of its publication date; a link addition order takes no body, and any field is refused. The order moves to pending_order_delivery_review and the buyer is asked to review it, or, for a post order whose scheduled publication date is still to come, to scheduled without telling the buyer yet. The URL is checked to be a URL without < or >, as the web does.

Parameters

Name In Type Description
id Required path string (uuid)
Idempotency-Key Required header string max length 255 A new key for each write, reused only to retry that same write. 1 to 255 printable ASCII characters without spaces (! to ~); any other value answers 400 invalid_idempotency_key.

Request body application/json, Optional

Field Type Description
url Required string (uri) The URL of the published post. It is checked to be a URL without < or >, as the web does; whether the post carries the links is not checked here.
publicationDateConfirmed Required boolean Must be true. Confirms the post is live, or for an order with a scheduled publication date, that it will be published on that date.

Responses

  • 200

    The order, delivered or scheduled.

  • 400

    The write has no Idempotency-Key header (code idempotency_key_required), the header is not 1 to 255 printable ASCII characters without spaces (code invalid_idempotency_key), or the body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The key is read-only (code insufficient_capability).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 409

    The order is not waiting for its delivery (code invalid_state), or the Idempotency-Key conflicts (codes idempotency_key_reused, idempotency_request_in_progress, idempotency_outcome_unknown).

  • 422

    The request is not valid (code validation_failed). details.fields holds the messages for each invalid field, by field name.

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X POST "https://tool.growwer.com/api/v1/seller/orders/$ORDER_ID/deliver" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://example.com",
  "publicationDateConfirmed": false
}'

Example response

{
  "id": "00000000-0000-0000-0000-000000000000",
  "reference": "string",
  "type": "post",
  "state": "string",
  "requiresYourAction": false,
  "availableActions": [
    "accept"
  ],
  "earnings": {
    "amount": 12500,
    "currency": "EUR"
  },
  "project": {
    "url": "string"
  },
  "media": {
    "id": "00000000-0000-0000-0000-000000000000",
    "url": "string"
  },
  "createdAt": "2026-01-01T00:00:00+00:00",
  "updatedAt": "2026-01-01T00:00:00+00:00",
  "scheduledPublicationDate": "2026-01-01T00:00:00+00:00",
  "delivery": {
    "url": "string",
    "publishedAt": "2026-01-01T00:00:00+00:00"
  },
  "briefing": {
    "title": "string",
    "language": "string",
    "links": [
      {
        "targetUrl": "string",
        "anchor": "string",
        "type": "dofollow"
      }
    ],
    "extraServices": [
      "list_on_homepage"
    ],
    "additionalInfo": "string",
    "writtenBy": "seller",
    "hasProposedDraft": false
  },
  "draft": {
    "reviewable": false,
    "hasFile": false
  },
  "stateHistory": [
    {
      "state": "string",
      "date": "2026-01-01T00:00:00+00:00",
      "reason": {
        "code": "other_reasons",
        "additionalInfo": "string"
      }
    }
  ],
  "channel": {
    "created": "api",
    "accepted": "api"
  }
}

POST /api/v1/seller/orders/{id}/deliver-draft

Deliver a draft

Delivers the draft of a post order placed on one of your medias whose draft you write (state pending_draft), as the web does, from a file you uploaded with POST /files. The order moves to pending_draft_review when the buyer reviews drafts, otherwise to pending_order_delivery. The file is used up once the draft is delivered; a refused request leaves it usable.

Parameters

Name In Type Description
id Required path string (uuid)
Idempotency-Key Required header string max length 255 A new key for each write, reused only to retry that same write. 1 to 255 printable ASCII characters without spaces (! to ~); any other value answers 400 invalid_idempotency_key.

Request body application/json, Required

Field Type Description
fileId Required string (uuid) A file you uploaded with POST /files and have not used yet. It must be an odt, doc, docx or txt file of up to 8 MB. It is used up once the draft is delivered.

Responses

  • 200

    The order, with its draft delivered.

  • 400

    The write has no Idempotency-Key header (code idempotency_key_required), the header is not 1 to 255 printable ASCII characters without spaces (code invalid_idempotency_key), or the body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The key is read-only (code insufficient_capability).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 409

    The order is not waiting for a draft you write (code invalid_state), or the Idempotency-Key conflicts (codes idempotency_key_reused, idempotency_request_in_progress, idempotency_outcome_unknown).

  • 422

    The body is not valid, or the file is not an odt, doc, docx or txt file of up to 8 MB (code validation_failed, with the messages of each field in details.fields), or fileId names no file you can use: it does not exist, has expired or was already used (code file_unusable).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X POST "https://tool.growwer.com/api/v1/seller/orders/$ORDER_ID/deliver-draft" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "fileId": "00000000-0000-0000-0000-000000000000"
}'

Example response

{
  "id": "00000000-0000-0000-0000-000000000000",
  "reference": "string",
  "type": "post",
  "state": "string",
  "requiresYourAction": false,
  "availableActions": [
    "accept"
  ],
  "earnings": {
    "amount": 12500,
    "currency": "EUR"
  },
  "project": {
    "url": "string"
  },
  "media": {
    "id": "00000000-0000-0000-0000-000000000000",
    "url": "string"
  },
  "createdAt": "2026-01-01T00:00:00+00:00",
  "updatedAt": "2026-01-01T00:00:00+00:00",
  "scheduledPublicationDate": "2026-01-01T00:00:00+00:00",
  "delivery": {
    "url": "string",
    "publishedAt": "2026-01-01T00:00:00+00:00"
  },
  "briefing": {
    "title": "string",
    "language": "string",
    "links": [
      {
        "targetUrl": "string",
        "anchor": "string",
        "type": "dofollow"
      }
    ],
    "extraServices": [
      "list_on_homepage"
    ],
    "additionalInfo": "string",
    "writtenBy": "seller",
    "hasProposedDraft": false
  },
  "draft": {
    "reviewable": false,
    "hasFile": false
  },
  "stateHistory": [
    {
      "state": "string",
      "date": "2026-01-01T00:00:00+00:00",
      "reason": {
        "code": "other_reasons",
        "additionalInfo": "string"
      }
    }
  ],
  "channel": {
    "created": "api",
    "accepted": "api"
  }
}

POST /api/v1/seller/orders/{id}/deliver-draft-update

Deliver a draft update

Delivers the draft update the buyer asked for on a post order placed on one of your medias whose draft you write (state pending_draft_update), as the web does, from a file you uploaded with POST /files. The order moves back to pending_draft_review. The file is used up once the draft is delivered; a refused request leaves it usable.

Parameters

Name In Type Description
id Required path string (uuid)
Idempotency-Key Required header string max length 255 A new key for each write, reused only to retry that same write. 1 to 255 printable ASCII characters without spaces (! to ~); any other value answers 400 invalid_idempotency_key.

Request body application/json, Required

Field Type Description
fileId Required string (uuid) A file you uploaded with POST /files and have not used yet. It must be an odt, doc, docx or txt file of up to 8 MB. It is used up once the draft is delivered.

Responses

  • 200

    The order, with its updated draft delivered.

  • 400

    The write has no Idempotency-Key header (code idempotency_key_required), the header is not 1 to 255 printable ASCII characters without spaces (code invalid_idempotency_key), or the body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The key is read-only (code insufficient_capability).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 409

    The buyer has not asked for an update of a draft you write (code invalid_state), or the Idempotency-Key conflicts (codes idempotency_key_reused, idempotency_request_in_progress, idempotency_outcome_unknown).

  • 422

    The body is not valid, or the file is not an odt, doc, docx or txt file of up to 8 MB (code validation_failed, with the messages of each field in details.fields), or fileId names no file you can use: it does not exist, has expired or was already used (code file_unusable).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X POST "https://tool.growwer.com/api/v1/seller/orders/$ORDER_ID/deliver-draft-update" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "fileId": "00000000-0000-0000-0000-000000000000"
}'

Example response

{
  "id": "00000000-0000-0000-0000-000000000000",
  "reference": "string",
  "type": "post",
  "state": "string",
  "requiresYourAction": false,
  "availableActions": [
    "accept"
  ],
  "earnings": {
    "amount": 12500,
    "currency": "EUR"
  },
  "project": {
    "url": "string"
  },
  "media": {
    "id": "00000000-0000-0000-0000-000000000000",
    "url": "string"
  },
  "createdAt": "2026-01-01T00:00:00+00:00",
  "updatedAt": "2026-01-01T00:00:00+00:00",
  "scheduledPublicationDate": "2026-01-01T00:00:00+00:00",
  "delivery": {
    "url": "string",
    "publishedAt": "2026-01-01T00:00:00+00:00"
  },
  "briefing": {
    "title": "string",
    "language": "string",
    "links": [
      {
        "targetUrl": "string",
        "anchor": "string",
        "type": "dofollow"
      }
    ],
    "extraServices": [
      "list_on_homepage"
    ],
    "additionalInfo": "string",
    "writtenBy": "seller",
    "hasProposedDraft": false
  },
  "draft": {
    "reviewable": false,
    "hasFile": false
  },
  "stateHistory": [
    {
      "state": "string",
      "date": "2026-01-01T00:00:00+00:00",
      "reason": {
        "code": "other_reasons",
        "additionalInfo": "string"
      }
    }
  ],
  "channel": {
    "created": "api",
    "accepted": "api"
  }
}

POST /api/v1/seller/orders/{id}/deliver-update

Deliver an update

Delivers the update the buyer asked for on an order placed on one of your medias (state pending_order_delivery_update), as the web does. A post order takes the URL of the published post, which may be the same as before, and the confirmation of its publication date; both must be sent. A link addition order takes no body, and any field is refused. The order moves back to pending_order_delivery_review and the buyer is asked to review it.

Parameters

Name In Type Description
id Required path string (uuid)
Idempotency-Key Required header string max length 255 A new key for each write, reused only to retry that same write. 1 to 255 printable ASCII characters without spaces (! to ~); any other value answers 400 invalid_idempotency_key.

Request body application/json, Optional

Field Type Description
url Required string (uri) The URL of the published post. It is checked to be a URL without < or >, as the web does; whether the post carries the links is not checked here.
publicationDateConfirmed Required boolean Must be true. Confirms the post is live, or for an order with a scheduled publication date, that it will be published on that date.

Responses

  • 200

    The order, with the update delivered.

  • 400

    The write has no Idempotency-Key header (code idempotency_key_required), the header is not 1 to 255 printable ASCII characters without spaces (code invalid_idempotency_key), or the body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The key is read-only (code insufficient_capability).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 409

    The buyer has not asked for an update (code invalid_state), or the Idempotency-Key conflicts (codes idempotency_key_reused, idempotency_request_in_progress, idempotency_outcome_unknown).

  • 422

    The request is not valid (code validation_failed). details.fields holds the messages for each invalid field, by field name.

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X POST "https://tool.growwer.com/api/v1/seller/orders/$ORDER_ID/deliver-update" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://example.com",
  "publicationDateConfirmed": false
}'

Example response

{
  "id": "00000000-0000-0000-0000-000000000000",
  "reference": "string",
  "type": "post",
  "state": "string",
  "requiresYourAction": false,
  "availableActions": [
    "accept"
  ],
  "earnings": {
    "amount": 12500,
    "currency": "EUR"
  },
  "project": {
    "url": "string"
  },
  "media": {
    "id": "00000000-0000-0000-0000-000000000000",
    "url": "string"
  },
  "createdAt": "2026-01-01T00:00:00+00:00",
  "updatedAt": "2026-01-01T00:00:00+00:00",
  "scheduledPublicationDate": "2026-01-01T00:00:00+00:00",
  "delivery": {
    "url": "string",
    "publishedAt": "2026-01-01T00:00:00+00:00"
  },
  "briefing": {
    "title": "string",
    "language": "string",
    "links": [
      {
        "targetUrl": "string",
        "anchor": "string",
        "type": "dofollow"
      }
    ],
    "extraServices": [
      "list_on_homepage"
    ],
    "additionalInfo": "string",
    "writtenBy": "seller",
    "hasProposedDraft": false
  },
  "draft": {
    "reviewable": false,
    "hasFile": false
  },
  "stateHistory": [
    {
      "state": "string",
      "date": "2026-01-01T00:00:00+00:00",
      "reason": {
        "code": "other_reasons",
        "additionalInfo": "string"
      }
    }
  ],
  "channel": {
    "created": "api",
    "accepted": "api"
  }
}

GET /api/v1/seller/orders/{id}/draft

Download the draft of an order you sell

The file of the current draft of a post order you sell. 404 when the order has no draft file (draft.hasFile is false) or the file cannot be read. Earlier drafts are not available.

Parameters

Name In Type Description
id Required path string (uuid)

Responses

  • 200

    The file, with its own content type and name.

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X GET "https://tool.growwer.com/api/v1/seller/orders/$ORDER_ID/draft" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -o draft

Answers the file itself (application/octet-stream), not JSON.

POST /api/v1/seller/orders/{id}/refute-draft-update

Refuse a draft update

Refuses the draft update the buyer asked for on a post order placed on one of your medias whose draft you write (state pending_draft_update), as the web does. The order moves back to pending_draft_review and the buyer is told your reason. A draft Growwer or the buyer writes cannot be refused here.

Parameters

Name In Type Description
id Required path string (uuid)
Idempotency-Key Required header string max length 255 A new key for each write, reused only to retry that same write. 1 to 255 printable ASCII characters without spaces (! to ~); any other value answers 400 invalid_idempotency_key.

Request body application/json, Required

Field Type Description
reason Required string min length 10, max length 2000 Why you refuse the buyer's request, which the buyer is told, 10 to 2000 characters once trimmed.

Responses

  • 200

    The order, with its draft back in review.

  • 400

    The write has no Idempotency-Key header (code idempotency_key_required), the header is not 1 to 255 printable ASCII characters without spaces (code invalid_idempotency_key), or the body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The key is read-only (code insufficient_capability).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 409

    The buyer has not asked for an update of a draft you write, including when Growwer or the buyer writes the draft (code invalid_state), or the Idempotency-Key conflicts (codes idempotency_key_reused, idempotency_request_in_progress, idempotency_outcome_unknown).

  • 422

    The request is not valid (code validation_failed). details.fields holds the messages for each invalid field, by field name.

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X POST "https://tool.growwer.com/api/v1/seller/orders/$ORDER_ID/refute-draft-update" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "reason": "example example"
}'

Example response

{
  "id": "00000000-0000-0000-0000-000000000000",
  "reference": "string",
  "type": "post",
  "state": "string",
  "requiresYourAction": false,
  "availableActions": [
    "accept"
  ],
  "earnings": {
    "amount": 12500,
    "currency": "EUR"
  },
  "project": {
    "url": "string"
  },
  "media": {
    "id": "00000000-0000-0000-0000-000000000000",
    "url": "string"
  },
  "createdAt": "2026-01-01T00:00:00+00:00",
  "updatedAt": "2026-01-01T00:00:00+00:00",
  "scheduledPublicationDate": "2026-01-01T00:00:00+00:00",
  "delivery": {
    "url": "string",
    "publishedAt": "2026-01-01T00:00:00+00:00"
  },
  "briefing": {
    "title": "string",
    "language": "string",
    "links": [
      {
        "targetUrl": "string",
        "anchor": "string",
        "type": "dofollow"
      }
    ],
    "extraServices": [
      "list_on_homepage"
    ],
    "additionalInfo": "string",
    "writtenBy": "seller",
    "hasProposedDraft": false
  },
  "draft": {
    "reviewable": false,
    "hasFile": false
  },
  "stateHistory": [
    {
      "state": "string",
      "date": "2026-01-01T00:00:00+00:00",
      "reason": {
        "code": "other_reasons",
        "additionalInfo": "string"
      }
    }
  ],
  "channel": {
    "created": "api",
    "accepted": "api"
  }
}

POST /api/v1/seller/orders/{id}/refute-update

Refuse an update

Refuses the update the buyer asked for on an order placed on one of your medias (state pending_order_delivery_update), as the web does. The order moves back to pending_order_delivery_review and the buyer is told your reason.

Parameters

Name In Type Description
id Required path string (uuid)
Idempotency-Key Required header string max length 255 A new key for each write, reused only to retry that same write. 1 to 255 printable ASCII characters without spaces (! to ~); any other value answers 400 invalid_idempotency_key.

Request body application/json, Required

Field Type Description
reason Required string min length 10, max length 2000 Why you refuse the buyer's request, which the buyer is told, 10 to 2000 characters once trimmed.

Responses

  • 200

    The order, back in review.

  • 400

    The write has no Idempotency-Key header (code idempotency_key_required), the header is not 1 to 255 printable ASCII characters without spaces (code invalid_idempotency_key), or the body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The key is read-only (code insufficient_capability).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 409

    The buyer has not asked for an update (code invalid_state), or the Idempotency-Key conflicts (codes idempotency_key_reused, idempotency_request_in_progress, idempotency_outcome_unknown).

  • 422

    The request is not valid (code validation_failed). details.fields holds the messages for each invalid field, by field name.

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X POST "https://tool.growwer.com/api/v1/seller/orders/$ORDER_ID/refute-update" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "reason": "example example"
}'

Example response

{
  "id": "00000000-0000-0000-0000-000000000000",
  "reference": "string",
  "type": "post",
  "state": "string",
  "requiresYourAction": false,
  "availableActions": [
    "accept"
  ],
  "earnings": {
    "amount": 12500,
    "currency": "EUR"
  },
  "project": {
    "url": "string"
  },
  "media": {
    "id": "00000000-0000-0000-0000-000000000000",
    "url": "string"
  },
  "createdAt": "2026-01-01T00:00:00+00:00",
  "updatedAt": "2026-01-01T00:00:00+00:00",
  "scheduledPublicationDate": "2026-01-01T00:00:00+00:00",
  "delivery": {
    "url": "string",
    "publishedAt": "2026-01-01T00:00:00+00:00"
  },
  "briefing": {
    "title": "string",
    "language": "string",
    "links": [
      {
        "targetUrl": "string",
        "anchor": "string",
        "type": "dofollow"
      }
    ],
    "extraServices": [
      "list_on_homepage"
    ],
    "additionalInfo": "string",
    "writtenBy": "seller",
    "hasProposedDraft": false
  },
  "draft": {
    "reviewable": false,
    "hasFile": false
  },
  "stateHistory": [
    {
      "state": "string",
      "date": "2026-01-01T00:00:00+00:00",
      "reason": {
        "code": "other_reasons",
        "additionalInfo": "string"
      }
    }
  ],
  "channel": {
    "created": "api",
    "accepted": "api"
  }
}

POST /api/v1/seller/orders/{id}/reject

Reject an order

Rejects an order placed on one of your medias that you have not accepted yet (state pending_acceptance), as the web does. The order moves to rejected_by_seller, its price returns to the buyer, and the buyer is told your reason.

Parameters

Name In Type Description
id Required path string (uuid)
Idempotency-Key Required header string max length 255 A new key for each write, reused only to retry that same write. 1 to 255 printable ASCII characters without spaces (! to ~); any other value answers 400 invalid_idempotency_key.

Request body application/json, Required

Field Type Description
reason Required object

Responses

  • 200

    The order, rejected.

  • 400

    The write has no Idempotency-Key header (code idempotency_key_required), the header is not 1 to 255 printable ASCII characters without spaces (code invalid_idempotency_key), or the body is not a JSON object (code invalid_json).

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 403

    The key is read-only (code insufficient_capability).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 409

    The order is not waiting for your acceptance (code invalid_state), or the Idempotency-Key conflicts (codes idempotency_key_reused, idempotency_request_in_progress, idempotency_outcome_unknown).

  • 422

    The request is not valid (code validation_failed). details.fields holds the messages for each invalid field, by field name.

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X POST "https://tool.growwer.com/api/v1/seller/orders/$ORDER_ID/reject" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "reason": {
    "name": "media_is_competition",
    "additionalInfo": "example example"
  }
}'

Example response

{
  "id": "00000000-0000-0000-0000-000000000000",
  "reference": "string",
  "type": "post",
  "state": "string",
  "requiresYourAction": false,
  "availableActions": [
    "accept"
  ],
  "earnings": {
    "amount": 12500,
    "currency": "EUR"
  },
  "project": {
    "url": "string"
  },
  "media": {
    "id": "00000000-0000-0000-0000-000000000000",
    "url": "string"
  },
  "createdAt": "2026-01-01T00:00:00+00:00",
  "updatedAt": "2026-01-01T00:00:00+00:00",
  "scheduledPublicationDate": "2026-01-01T00:00:00+00:00",
  "delivery": {
    "url": "string",
    "publishedAt": "2026-01-01T00:00:00+00:00"
  },
  "briefing": {
    "title": "string",
    "language": "string",
    "links": [
      {
        "targetUrl": "string",
        "anchor": "string",
        "type": "dofollow"
      }
    ],
    "extraServices": [
      "list_on_homepage"
    ],
    "additionalInfo": "string",
    "writtenBy": "seller",
    "hasProposedDraft": false
  },
  "draft": {
    "reviewable": false,
    "hasFile": false
  },
  "stateHistory": [
    {
      "state": "string",
      "date": "2026-01-01T00:00:00+00:00",
      "reason": {
        "code": "other_reasons",
        "additionalInfo": "string"
      }
    }
  ],
  "channel": {
    "created": "api",
    "accepted": "api"
  }
}

Seller wallet

Your seller balance, its movements and documents, and your withdrawals.

GET /api/v1/wallets/seller

Get your seller wallet

What your sales have earned: the balance you can withdraw now and what is held until orders settle. restricted is true while your seller account is restricted; the reason is not published here, and support can tell you more.

Responses

  • 200

    Your seller wallet.

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X GET "https://tool.growwer.com/api/v1/wallets/seller" \
  -H "Authorization: Bearer $GROWWER_API_KEY"

Example response

{
  "balance": {
    "available": {
      "amount": 12500,
      "currency": "EUR"
    },
    "pending": {
      "amount": 12500,
      "currency": "EUR"
    }
  },
  "restricted": false
}

GET /api/v1/wallets/seller/transactions

List your seller wallet movements

Every movement of your seller wallet, newest first: earnings from orders, withdrawals and manual corrections. An earning names its order (orderId) and a withdrawal its request (withdrawalId).

Parameters

Name In Type Description
limit query integer min 1, max 100, default 20 Movements per page, 1 to 100.
offset query integer min 0, default 0 Movements to skip.

Responses

  • 200

    A page of your movements and how many there are in all.

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 422

    The request is not valid (code validation_failed). details.fields holds the messages for each invalid field, by field name.

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X GET "https://tool.growwer.com/api/v1/wallets/seller/transactions" \
  -H "Authorization: Bearer $GROWWER_API_KEY"

Example response

{
  "items": [
    {
      "id": "00000000-0000-0000-0000-000000000000",
      "type": "string",
      "state": "pending_confirmation",
      "direction": "credit",
      "amount": {
        "amount": 12500,
        "currency": "EUR"
      },
      "description": "string",
      "orderId": "00000000-0000-0000-0000-000000000000",
      "withdrawalId": "00000000-0000-0000-0000-000000000000",
      "hasDocument": false,
      "createdAt": "2026-01-01T00:00:00+00:00",
      "updatedAt": "2026-01-01T00:00:00+00:00"
    }
  ],
  "total": 0
}

GET /api/v1/wallets/seller/transactions/{id}/document

Download the document of a seller wallet movement

The document Growwer attached to a movement of your seller wallet. 404 when the movement has none (hasDocument is false) or the file cannot be read.

Parameters

Name In Type Description
id Required path string (uuid)

Responses

  • 200

    The document, with its own content type and name.

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X GET "https://tool.growwer.com/api/v1/wallets/seller/transactions/$TRANSACTION_ID/document" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -o document

Answers the file itself (application/octet-stream), not JSON.

GET /api/v1/withdrawals

List your withdrawals

The withdrawals you requested from your seller wallet, newest first. Requesting a withdrawal is not available through the API; use the seller wallet page.

Parameters

Name In Type Description
state query string Only the withdrawals in one of these states, comma-separated: started, approved, rejected. An unknown state is refused with 422.
limit query integer min 1, max 100, default 20 Withdrawals per page, 1 to 100.
offset query integer min 0, default 0 Withdrawals to skip.

Responses

  • 200

    A page of your withdrawals and how many match in all.

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 422

    The request is not valid (code validation_failed). details.fields holds the messages for each invalid field, by field name.

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X GET "https://tool.growwer.com/api/v1/withdrawals" \
  -H "Authorization: Bearer $GROWWER_API_KEY"

Example response

{
  "items": [
    {
      "id": "00000000-0000-0000-0000-000000000000",
      "state": "started",
      "amount": {
        "amount": 12500,
        "currency": "EUR"
      },
      "method": "bank_transfer",
      "hasInvoice": false,
      "createdAt": "2026-01-01T00:00:00+00:00"
    }
  ],
  "total": 0
}

GET /api/v1/withdrawals/{id}/invoice

Download the invoice of a withdrawal

The invoice you uploaded for one of your withdrawals, named withdrawal-{id}-invoice.pdf. 404 when the withdrawal has none (hasInvoice is false); 503 storage_unavailable when the file cannot be read right now.

Parameters

Name In Type Description
id Required path string (uuid)

Responses

  • 200

    The invoice file.

  • 401

    The API key is missing, malformed, unknown, revoked or expired, or its account cannot sign in (code unauthorized).

  • 404

    The resource does not exist, or it is not yours (code not_found).

  • 429

    Over the rate limit (code rate_limit_exceeded), or too many failed authentications (code too_many_failed_authentications): with this key from this address, or from this address with any key; a valid key is never refused for it. On the catalogue operations, also when the response would take the account beyond the units of the catalogue it may spend per UTC day (code catalogue_daily_cap_reached): each new media costs one unit and a response that holds no new media, an empty one or a count included, costs one unless the same request was answered today while the media lists, topics and language of its project stayed as they were, which is free; details.remaining says how many units still fit today, details.limit is the daily cap that applies and details.tier is full, or trial for an account without a recent completed order, whose quota rises to the full cap after one; Retry-After is the time to the next UTC midnight, and a request already answered today stays readable. On the managed service requests, also when you sent as many requests of that kind as you may per UTC day (code managed_service_request_limit_reached): Retry-After is the time to the next UTC midnight. On POST /seller/medias, also when you sent as many medias that are already registered as you may per UTC day (code media_duplicate_limit_reached): Retry-After is the time to the next UTC midnight, and medias not registered yet are still accepted.

  • 500

    An unexpected error (code internal_error).

  • 503

    The rate limiter cannot be reached, so the call is refused (code rate_limit_unavailable), or a stored file cannot be read right now (code storage_unavailable). Retry later.

curl -X GET "https://tool.growwer.com/api/v1/withdrawals/$WITHDRAWAL_ID/invoice" \
  -H "Authorization: Bearer $GROWWER_API_KEY" \
  -o invoice

Answers the file itself (application/octet-stream), not JSON.

Questions about the Growwer API

  • A REST API over your Growwer account. Most of what you do in Growwer as a buyer or a seller is reachable over HTTP: projects, the media catalogue, orders, the managed service and, on the seller side, your media and the orders they receive. You authenticate with an API key you create yourself.
  • Any Growwer account, on the API keys page of your profile. Each key can read, or read and write, a choice fixed when you create it, and the full key is shown only once. See how keys work.
  • Placing an order spends your buyer balance, so it needs a read and write key. A key can only spend the balance already in your wallet: top-ups and withdrawal requests stay on the web. Estimate an order first to compare its price with your balance, and reuse its Idempotency-Key to retry, so a lost response never becomes a second order. Accepting a managed service proposal charges nothing itself; the team then places the order, which spends your balance.
  • 600 requests a minute per user and 300 per key. Over the limit, the API answers 429 with a Retry-After header. The catalogue has a daily cap of its own: 5,000 units a day for accounts with a recent completed order, as buyer or as seller, and a trial quota of 100 a day for any other account. See how the cap is counted.
  • No. To follow your orders, poll GET /api/v1/orders with sort=updatedAt and updatedSince set to the last updatedAt you saw.
  • Both. Official clients exist for TypeScript, Python, PHP, Go, Java, .NET and Ruby, generated from the public OpenAPI contract: the client libraries section has the install commands. To work from Claude, ChatGPT, Cursor or another AI assistant without writing code, connect the Growwer MCP.

Connect your systems to Growwer

Create your account, then create an API key on your profile.