RTB Architecture

    Prebid Server Stored Requests and Configuration: Formats, Merge Order and Backends

    Published · Last updated
    Next scheduled review:

    A Prebid Server stored request is a block of OpenRTB JSON kept on the server and merged into an incoming request by ID. Top-level stored requests fill the request root; stored imps fill one impression. Your Prebid Server config decides where they live, in files, SQL, HTTP or S3, and how long they are cached (Prebid docs).

    Key terms on this page: stored request · Prebid Server · OpenRTB · bidder adapter · Prebid.js · tmax

    Contents

    Key facts

    • Two kinds, one merge rule. A top-level ID goes in ext.prebid.storedrequest.id and an impression ID in imp[].ext.prebid.storedrequest.id. Conflicts are settled with JSON Merge Patch, and the incoming request wins (Prebid.org, stored requests for Java, accessed 9 November 2026).
    • AMP depends on them. The /openrtb2/amp endpoint requires a tag_id that points to a stored request with exactly one impression and no app object (AMP endpoint docs, accessed 9 November 2026).
    • Backends differ by build. PBS-Go reads files, Postgres or MySQL, and HTTP; PBS-Java adds Amazon S3 (stored-requests.md; config-app.md, both accessed 9 November 2026).
    • Lookups have a tight budget. The default stored request timeout is 50 ms in PBS-Go and 100 ms in PBS-Java (config.go; application.yaml, both accessed 9 November 2026).
    • The Go caching layer is changing. PBS-Go 4.11.0, released on 26 October 2026, added an opt-in "Fetchers 2.0" cache, so far for account data only (v4.11.0 release; stored_requests.go).
    Jump to the table

    Definition

    Stored request (Prebid Server): a named fragment of an OpenRTB 2.x bid request, held in a data store that Prebid Server reads but never writes. The server looks it up by ID at auction time and merges it into the incoming request (Prebid Server database, accessed 9 November 2026).

    Prebid's database guide is blunt about ownership: "PBS never writes this data, only reads it." The host decides where the data comes from and how it gets into each region.

    Why stored requests exist

    Stored requests move ad unit setup from the client to the server. Prebid's docs give the mobile case: hard-coding bidders and parameters into an app means a new app release for every change (stored requests overview, accessed 9 November 2026). The Prebid SDK sends a skeleton request instead, and ad ops change the stored JSON.

    AMP is the second case. An AMP page sends only a tag_id and a few query parameters, such as width, height and consent. Prebid Server loads the stored request for that tag_id and builds the OpenRTB request from it (AMP endpoint docs).

    Web pages can use them too, for the same reason that header bidding moved logic out of the ad server: control over who bids, as explained in header bidding explained. In Prebid.js, an ad unit can carry ortb2Imp.ext.prebid.storedrequest.id, and the bids array can then be left out (Prebid Server adapter docs, accessed 9 November 2026). That keeps bidder IDs out of page source. Our Prebid.js guide covers the client side.

    How the layers merge

    Up to four layers can shape the request that reaches the auction. Each later layer overrides the one before it.

    4 of 4 rows

    Columns (4)

    1 (lowest)

    Default request

    default_request.file.name (Go) or default-request.file.path (Java), read once at startup

    Server-wide aliases, a default tmax, GDPR defaults

    2

    Top-level stored request

    ext.prebid.storedrequest.id

    tmax, currency, price granularity, targeting and cache flags

    3

    Stored imp

    imp[].ext.prebid.storedrequest.id

    Formats and ext.prebid.bidder parameters for one slot

    4 (highest)

    Incoming HTTP request

    The request body itself

    Page URL, device, user, consent, live overrides

    1 (lowest)

    Layer

    Default request

    Where the ID or file is set

    default_request.file.name (Go) or default-request.file.path (Java), read once at startup

    Typical contents

    Server-wide aliases, a default tmax, GDPR defaults

    2

    Layer

    Top-level stored request

    Where the ID or file is set

    ext.prebid.storedrequest.id

    Typical contents

    tmax, currency, price granularity, targeting and cache flags

    3

    Layer

    Stored imp

    Where the ID or file is set

    imp[].ext.prebid.storedrequest.id

    Typical contents

    Formats and ext.prebid.bidder parameters for one slot

    4 (highest)

    Layer

    Incoming HTTP request

    Where the ID or file is set

    The request body itself

    Typical contents

    Page URL, device, user, consent, live overrides

    Download JSON

    Table by AdSovereign, CC BY 4.0.

    Sources: default request docs and stored requests for Go, both accessed 9 November 2026. Prebid applies the top-level patch first, then the imp patches. The merge is not recursive: a stored request that contains imps with their own stored IDs will not have those resolved.

    A worked example: three documents, one auction

    A news site wants Prebid.js to send almost nothing. It keeps site defaults in one stored request and the leaderboard setup in one stored imp. The IDs and bidder parameters below are placeholders; the parameter names follow each adapter's published JSON schema in the prebid-server repository, explained in Prebid Server bidder adapters.

    The incoming request from the page:

    {
      "id": "auc-7f3a",
      "tmax": 700,
      "site": {
        "page": "https://news.example.com/sport/match-report",
        "publisher": { "id": "1001" }
      },
      "imp": [
        { "id": "div-top", "ext": { "prebid": { "storedrequest": { "id": "news-leaderboard" } } } }
      ],
      "ext": { "prebid": { "storedrequest": { "id": "news-site-defaults" } } }
    }

    The top-level stored request, saved as stored_requests/news-site-defaults.json:

    {
      "tmax": 1000,
      "cur": ["EUR"],
      "ext": {
        "prebid": {
          "targeting": { "pricegranularity": "medium", "includewinners": true },
          "cache": { "bids": {} }
        }
      }
    }

    The stored imp, saved as stored_imps/news-leaderboard.json:

    {
      "banner": { "format": [ { "w": 728, "h": 90 }, { "w": 970, "h": 250 } ] },
      "ext": {
        "prebid": {
          "bidder": {
            "pubmatic": { "publisherId": "156209", "adSlot": "news-leaderboard" },
            "ix": { "siteId": "123456" }
          }
        }
      }
    }

    What the auction sees after the merge:

    {
      "id": "auc-7f3a",
      "tmax": 700,
      "cur": ["EUR"],
      "site": {
        "page": "https://news.example.com/sport/match-report",
        "publisher": { "id": "1001" }
      },
      "imp": [
        {
          "id": "div-top",
          "banner": { "format": [ { "w": 728, "h": 90 }, { "w": 970, "h": 250 } ] },
          "ext": {
            "prebid": {
              "storedrequest": { "id": "news-leaderboard" },
              "bidder": {
                "pubmatic": { "publisherId": "156209", "adSlot": "news-leaderboard" },
                "ix": { "siteId": "123456" }
              }
            }
          }
        }
      ],
      "ext": {
        "prebid": {
          "storedrequest": { "id": "news-site-defaults" },
          "targeting": { "pricegranularity": "medium", "includewinners": true },
          "cache": { "bids": {} }
        }
      }
    }

    Three things happened. The tmax value stayed at 700 because the page sent it, so the stored 1000 lost. The cur field and the targeting block came from the stored request because the page sent neither. The bidder parameters arrived from the stored imp, so neither bidder ID appears in page source. To check a real merge, send the request with "test": 1 and read ext.debug.resolvedrequest, as shown in Prebid Server analytics and debugging.

    Backends and their keys

    8 of 8 rows

    Columns (3)

    Files

    stored_requests.filesystem.enabled, directorypath (default ./stored_requests/data/by_id, with stored_requests/, stored_imps/ and accounts/ folders)

    settings.filesystem.stored-requests-dir, stored-imps-dir, settings-filename

    SQL

    stored_requests.database.connection.driver (postgres or mysql), fetcher.query

    settings.database.type (mysql or postgres), stored-requests-query, amp-stored-requests-query

    Query placeholders

    $REQUEST_ID_LIST, $IMP_ID_LIST, $LAST_UPDATED

    %REQUEST_ID_LIST%, %IMP_ID_LIST%

    Columns returned

    ID, JSON body, type (request or imp)

    account ID, ID, JSON body, type

    HTTP

    stored_requests.http.endpoint, amp_endpoint

    settings.http.endpoint, amp-endpoint

    Amazon S3

    Not offered

    settings.s3.bucket, region, stored-requests-dir, stored-imps-dir

    Cache

    in_memory_cache (type lru, unbounded or none; sizes in bytes)

    settings.in-memory-cache (cache-size in entries, ttl-seconds)

    Live updates

    http_events polling, or database.poll_for_updates

    in-memory-cache.http-update, database-update, s3-update, admin endpoints

    Files

    PBS-Go keys

    stored_requests.filesystem.enabled, directorypath (default ./stored_requests/data/by_id, with stored_requests/, stored_imps/ and accounts/ folders)

    PBS-Java keys

    settings.filesystem.stored-requests-dir, stored-imps-dir, settings-filename

    SQL

    PBS-Go keys

    stored_requests.database.connection.driver (postgres or mysql), fetcher.query

    PBS-Java keys

    settings.database.type (mysql or postgres), stored-requests-query, amp-stored-requests-query

    Query placeholders

    PBS-Go keys

    $REQUEST_ID_LIST, $IMP_ID_LIST, $LAST_UPDATED

    PBS-Java keys

    %REQUEST_ID_LIST%, %IMP_ID_LIST%

    Columns returned

    PBS-Go keys

    ID, JSON body, type (request or imp)

    PBS-Java keys

    account ID, ID, JSON body, type

    HTTP

    PBS-Go keys

    stored_requests.http.endpoint, amp_endpoint

    PBS-Java keys

    settings.http.endpoint, amp-endpoint

    Amazon S3

    PBS-Go keys

    Not offered

    PBS-Java keys

    settings.s3.bucket, region, stored-requests-dir, stored-imps-dir

    Cache

    PBS-Go keys

    in_memory_cache (type lru, unbounded or none; sizes in bytes)

    PBS-Java keys

    settings.in-memory-cache (cache-size in entries, ttl-seconds)

    Live updates

    PBS-Go keys

    http_events polling, or database.poll_for_updates

    PBS-Java keys

    in-memory-cache.http-update, database-update, s3-update, admin endpoints

    Download JSON

    Table by AdSovereign, CC BY 4.0.

    The extra column in Java matters. Because the account ID comes back with each row, PBS-Java can keep stored request IDs unique per account, a feature Prebid lists as Java-only (feature index, accessed 9 November 2026). The other differences between the two builds are compared in Prebid Server Go vs Java.

    An annotated PBS-Go configuration

    PBS-Go reads configuration from environment variables, then pbs.json, then pbs.yaml, in that order of precedence, from the app directory or /etc/config (configuration.md, accessed 9 November 2026). This block reads stored data from Postgres, warms the cache at startup and polls for changes every 30 seconds.

    # pbs.yaml (PBS-Go 4.11.x). Each key also works as an env var, e.g. PBS_STORED_REQUESTS_TIMEOUT_MS.
    stored_requests_timeout_ms: 50            # default 50; must be > 0
    stored_requests:
      database:
        connection:
          driver: postgres                    # postgres or mysql
          host: "pbs-db.internal"             # yours: a read replica in the same region
          port: 5432
          dbname: "pbs"
          user: "pbs_reader"                  # read-only role; PBS never writes
          password: ""                        # yours: inject via PBS_STORED_REQUESTS_DATABASE_CONNECTION_PASSWORD
        fetcher:
          query: "SELECT id, data, 'request' AS type FROM stored_requests WHERE id IN $REQUEST_ID_LIST UNION ALL SELECT id, data, 'imp' AS type FROM stored_imps WHERE id IN $IMP_ID_LIST"
          amp_query: "SELECT id, data, 'request' AS type FROM stored_requests WHERE id IN $REQUEST_ID_LIST"
        initialize_caches:
          timeout_ms: 2000                    # required when query is set
          query: "SELECT id, data, 'request' AS type FROM stored_requests UNION ALL SELECT id, data, 'imp' AS type FROM stored_imps"
        poll_for_updates:
          refresh_rate_seconds: 30            # must be > 0 when query is set
          timeout_ms: 500
          query: "SELECT id, data, 'request' AS type FROM stored_requests WHERE updated_at > $LAST_UPDATED UNION ALL SELECT id, data, 'imp' AS type FROM stored_imps WHERE updated_at > $LAST_UPDATED"
      in_memory_cache:
        type: lru                             # default none
        ttl_seconds: 0                        # 0 = no expiry; rely on polling to refresh
        request_cache_size_bytes: 52428800    # 50 MB
        imp_cache_size_bytes: 104857600       # 100 MB
    accounts:
      filesystem:
        enabled: true                         # reads ./stored_requests/data/by_id/accounts/{id}.json
    default_request:
      type: file
      file:
        name: "/etc/config/default-request.json"
    gdpr:
      default_value: "1"                      # required; no default

    The validation rules in the comments come from the code. PBS-Go rejects an initialize_caches query without a positive timeout, a cache-warming query that contains $ wildcards, and a polling query without $LAST_UPDATED (stored_requests.go). Older pages on docs.prebid.org still show a stored_requests.postgres block with %REQUEST_ID_LIST%; the repository guide uses the database block above.

    An account file for that setup, saved as accounts/1001.json, uses the JSON names from PBS-Go's account structure (account.go, accessed 9 November 2026):

    {
      "id": "1001",
      "disabled": false,
      "debug_allow": true,
      "cookie_sync": { "default_limit": 6, "max_limit": 10, "default_coop_sync": true },
      "price_floors": { "enabled": true, "enforce_floors_rate": 100 }
    }

    How PBS uses the floors block is covered in our floor price strategy guide, and the cookie sync settings in Prebid Server cookie sync.

    The same setup in PBS-Java

    PBS-Java is a Spring Boot application. Values in your file override the bundled application.yaml, which you add with --spring.config.additional-location (PBS-Java config.md, accessed 9 November 2026).

    # prebid-config.yaml (PBS-Java 4.5.x)
    auction:
      stored-requests-timeout-ms: 100         # Java default 100
    settings:
      database:
        type: postgres                        # or mysql
        host: "pbs-db.internal"               # yours
        port: 5432
        dbname: "pbs"
        user: "pbs_reader"
        password: ""                          # yours: inject from a secret store
        stored-requests-query: "SELECT account_id, id, data, 'request' AS type FROM stored_requests WHERE id IN (%REQUEST_ID_LIST%) UNION ALL SELECT account_id, id, data, 'imp' AS type FROM stored_imps WHERE id IN (%IMP_ID_LIST%)"
        amp-stored-requests-query: "SELECT account_id, id, data, 'request' AS type FROM stored_requests WHERE id IN (%REQUEST_ID_LIST%)"
        account-query: "SELECT config FROM accounts WHERE id = %ACCOUNT_ID% LIMIT 1"
      in-memory-cache:
        cache-size: 10000                     # entries, not bytes
        ttl-seconds: 360
        database-update:
          init-query: "SELECT account_id, id, data, 'request' AS type FROM stored_requests UNION ALL SELECT account_id, id, data, 'imp' AS type FROM stored_imps"
          update-query: "SELECT account_id, id, data, 'request' AS type FROM stored_requests WHERE updated_at > $1 UNION ALL SELECT account_id, id, data, 'imp' AS type FROM stored_imps WHERE updated_at > $1"
          refresh-rate: 30000                 # ms
          timeout: 2000                       # ms

    The column order is fixed: account ID first, then ID, body and type (stored requests for Java). In update-query, Postgres takes $1 and MySQL takes ? for the last-update time (config-app.md). PBS-Java also exposes /storedrequests/openrtb2 and /storedrequests/amp on the admin port 8060 to push or invalidate cache entries, disabled by default (application.yaml).

    A schema to start from

    Prebid deliberately defines no schema. It defines only the columns a query must return, so you can add a few tables to a database you already run (Prebid Server database). The Postgres tables below match both configurations above. They are an example, not a Prebid standard.

    CREATE TABLE stored_requests (
      account_id  text        NOT NULL,
      id          text        PRIMARY KEY,
      data        jsonb       NOT NULL,
      updated_at  timestamptz NOT NULL DEFAULT now()
    );
    CREATE TABLE stored_imps (
      account_id  text        NOT NULL,
      id          text        PRIMARY KEY,
      data        jsonb       NOT NULL,
      updated_at  timestamptz NOT NULL DEFAULT now()
    );
    CREATE INDEX stored_requests_updated_idx ON stored_requests (updated_at);
    CREATE INDEX stored_imps_updated_idx ON stored_imps (updated_at);

    The updated_at index serves the polling query, which runs on every Prebid Server instance every refresh period. Prebid also notes that no open-source tool fills this database; each host builds its own pipeline from its ad ops system (Prebid Server hosting, accessed 9 November 2026).

    Prebid notes that hosted services from its members "provide tools to manage stored requests" (Prebid Server overview); Magnite Prebid Server and PubMatic OpenWrap are two such services. That pipeline is real work, and it is one of the line items to weigh in the Prebid Server self-hosting guide and the self-hosted Prebid Server profile. It is also where much of the control that the publisher independence hub argues for actually lives.

    The HTTP backend contract

    If your ad ops system already has an API, PBS-Go can call it instead of a database. The fetcher sends GET {endpoint}?request-id=req1&imp-id=imp1&imp-id=imp2 and expects JSON keyed by ID, with null for anything not found (fetcher.go, accessed 9 November 2026).

    {
      "requests": {
        "news-site-defaults": { "tmax": 1000, "cur": ["EUR"] }
      },
      "imps": {
        "news-leaderboard": { "banner": { "format": [ { "w": 728, "h": 90 } ] } },
        "news-mpu": null
      }
    }

    PBS-Go 4.0.0 made that repeated-parameter format, compliant with RFC 3986, the default. Hosts on the older request-ids=["req1"] format must switch it back with use_rfc3986_compliant_request_builder: false (v4.0.0 release, 6 March 2026; config.go).

    Common mistakes

    1. Expecting nested resolution. A stored request that lists imps with their own stored IDs will not resolve them. Put stored imp IDs in the incoming request.
    2. Sending imp objects inside a top-level stored request. Prebid's Java guide advises against it; keep imps in stored imps.
    3. AMP stored requests with more than one imp or an app object. The AMP endpoint rejects both (AMP endpoint docs).
    4. Copying the old Go query syntax. %REQUEST_ID_LIST% belongs to PBS-Java. Current PBS-Go uses $REQUEST_ID_LIST under stored_requests.database.
    5. Sizing caches in the wrong unit. PBS-Go caches are sized in bytes; PBS-Java's cache-size counts entries (SettingsCache.java).
    6. Polling a primary database from every node. Each instance runs the update query. Point Prebid Server at regional read replicas, as Prebid's hosting guide suggests.
    7. Keeping secrets in the YAML file. Pass database passwords through environment variables or a secret store. Our AWS and Docker reference setup shows one way.
    8. Two fetchers left on after a migration. Both builds treat several backends as ordered fallbacks. Prebid says this "isn't a great idea for Prod in the long-term" (stored requests for Go).

    FAQ

    What is a Prebid Server stored request?
    It is a saved piece of OpenRTB JSON that Prebid Server merges into an incoming bid request by ID. It lets apps, AMP pages and websites send short requests while ad ops keep bidder setup on the server (stored requests overview).
    What is the difference between a stored request and a stored imp?
    A top-level stored request patches the root of the bid request, such as tmax, currency and targeting. A stored imp patches one impression, usually formats and bidder parameters. Prebid applies the top-level patch first and the imp patches after it.
    Where does Prebid Server keep stored requests?
    In files, Postgres, MySQL or an HTTP service for both builds, plus Amazon S3 for PBS-Java. An in-memory cache sits in front of each backend (stored-requests.md; config-app.md).
    Do AMP pages need stored requests?
    Yes. The AMP endpoint builds the whole bid request from the stored request named by tag_id, then adds size, URL and consent from the query string (AMP endpoint docs).
    How do I update a stored request without restarting Prebid Server?
    Change the row in your data store and let the server pick it up. PBS-Go polls with poll_for_updates or http_events; PBS-Java polls with database-update or http-update, or you push changes to its admin endpoints. The default request file is the exception; it is read only at startup (default request docs).
    How can I see the merged request?
    Send the auction with "test": 1. Prebid Server then returns the merged request in ext.debug.resolvedrequest (auction endpoint docs, accessed 9 November 2026).

    How this works on owned infrastructure

    Stored requests and account settings are the control plane of a Prebid Server cluster: which bidders run on which slot, at what floor, with which consent defaults. On a managed host, that data sits in the vendor's database and changes go through the vendor's tools. AdSovereign deploys the sell-side stack, including Prebid Server and its stored-request database, in an environment you operate, so ad ops edit the same tables your engineers back up and audit, and the read replicas sit in the regions you choose. The architecture page shows where the configuration store sits, and integrations lists the bidder connections it can describe.

    Sources

    1. Prebid.org, "Prebid Server Stored Requests" (accessed 9 November 2026). https://docs.prebid.org/prebid-server/features/pbs-storedreqs.html
    2. Prebid.org, "Setting Up Stored Requests for Go" (accessed 9 November 2026). https://docs.prebid.org/prebid-server/features/pbs-storedreqs-go.html
    3. Prebid.org, "Setting Up Stored Requests for Java" (accessed 9 November 2026). https://docs.prebid.org/prebid-server/features/pbs-storedreqs-java.html
    4. Prebid.org, "Prebid Server Database" (accessed 9 November 2026). https://docs.prebid.org/prebid-server/hosting/pbs-database.html
    5. Prebid.org, "Server Based Global Default Request" (accessed 9 November 2026). https://docs.prebid.org/prebid-server/features/pbs-default-request.html
    6. Prebid.org, "Prebid Server Endpoints: /openrtb2/amp" (accessed 9 November 2026). https://docs.prebid.org/prebid-server/endpoints/openrtb2/pbs-endpoint-amp.html
    7. Prebid.org, "Prebid Server Endpoints: /openrtb2/auction" (accessed 9 November 2026). https://docs.prebid.org/prebid-server/endpoints/openrtb2/pbs-endpoint-auction.html
    8. Prebid.org, "Prebid Server Adapter (s2sConfig)" (accessed 9 November 2026). https://docs.prebid.org/dev-docs/modules/prebidServer.html
    9. Prebid.org, "Prebid Server Feature Index" (accessed 9 November 2026). https://docs.prebid.org/prebid-server/features/pbs-feature-idx.html
    10. Prebid.org, "Hosting a Prebid Server Cluster" (accessed 9 November 2026). https://docs.prebid.org/prebid-server/hosting/pbs-hosting.html
    More sources (4)
    1. GitHub, prebid/prebid-server, docs/developers/stored-requests.md, docs/developers/configuration.md, config/stored_requests.go, config/config.go, config/account.go, stored_requests/backends/http_fetcher/fetcher.go (master, accessed 9 November 2026). https://github.com/prebid/prebid-server
    2. GitHub, prebid/prebid-server-java, docs/config.md, docs/config-app.md, src/main/resources/application.yaml (master, accessed 9 November 2026). https://github.com/prebid/prebid-server-java
    3. GitHub, prebid/prebid-server releases v4.0.0 (6 March 2026) and v4.11.0 (26 October 2026). https://github.com/prebid/prebid-server/releases
    4. IETF, RFC 7386, "JSON Merge Patch" (October 2014). https://www.rfc-editor.org/rfc/rfc7386.html