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.idand an impression ID inimp[].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/ampendpoint requires atag_idthat points to a stored request with exactly one impression and noappobject (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).
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
1 (lowest) | Default request |
| Server-wide aliases, a default |
2 | Top-level stored request |
|
|
3 | Stored imp |
| Formats and |
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) ordefault-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.bidderparameters 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
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
Files |
|
|
SQL |
|
|
Query placeholders |
|
|
Columns returned | ID, JSON body, type ( | account ID, ID, JSON body, type |
HTTP |
|
|
Amazon S3 | Not offered |
|
Cache |
|
|
Live updates |
|
|
Files
- PBS-Go keys
stored_requests.filesystem.enabled,directorypath(default./stored_requests/data/by_id, withstored_requests/,stored_imps/andaccounts/folders)- PBS-Java keys
settings.filesystem.stored-requests-dir,stored-imps-dir,settings-filename
SQL
- PBS-Go keys
stored_requests.database.connection.driver(postgresormysql),fetcher.query- PBS-Java keys
settings.database.type(mysqlorpostgres),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 (
requestorimp)- 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(typelru,unboundedornone; sizes in bytes)- PBS-Java keys
settings.in-memory-cache(cache-sizein entries,ttl-seconds)
Live updates
- PBS-Go keys
http_eventspolling, ordatabase.poll_for_updates- PBS-Java keys
in-memory-cache.http-update,database-update,s3-update, admin endpoints
Table by AdSovereign, CC BY 4.0.
Sources: PBS-Go stored-requests.md, stored_requests.go, config.go, PBS-Java config-app.md and stored requests for Java, all accessed 9 November 2026.
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 defaultThe 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 # msThe 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
- 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.
- Sending
impobjects inside a top-level stored request. Prebid's Java guide advises against it; keep imps in stored imps. - AMP stored requests with more than one imp or an
appobject. The AMP endpoint rejects both (AMP endpoint docs). - Copying the old Go query syntax.
%REQUEST_ID_LIST%belongs to PBS-Java. Current PBS-Go uses$REQUEST_ID_LISTunderstored_requests.database. - Sizing caches in the wrong unit. PBS-Go caches are sized in bytes; PBS-Java's
cache-sizecounts entries (SettingsCache.java). - 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.
- 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.
- 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?
What is the difference between a stored request and a stored imp?
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?
Do AMP pages need stored requests?
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?
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?
"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
- Prebid.org, "Prebid Server Stored Requests" (accessed 9 November 2026). https://docs.prebid.org/prebid-server/features/pbs-storedreqs.html
- Prebid.org, "Setting Up Stored Requests for Go" (accessed 9 November 2026). https://docs.prebid.org/prebid-server/features/pbs-storedreqs-go.html
- Prebid.org, "Setting Up Stored Requests for Java" (accessed 9 November 2026). https://docs.prebid.org/prebid-server/features/pbs-storedreqs-java.html
- Prebid.org, "Prebid Server Database" (accessed 9 November 2026). https://docs.prebid.org/prebid-server/hosting/pbs-database.html
- Prebid.org, "Server Based Global Default Request" (accessed 9 November 2026). https://docs.prebid.org/prebid-server/features/pbs-default-request.html
- Prebid.org, "Prebid Server Endpoints: /openrtb2/amp" (accessed 9 November 2026). https://docs.prebid.org/prebid-server/endpoints/openrtb2/pbs-endpoint-amp.html
- Prebid.org, "Prebid Server Endpoints: /openrtb2/auction" (accessed 9 November 2026). https://docs.prebid.org/prebid-server/endpoints/openrtb2/pbs-endpoint-auction.html
- Prebid.org, "Prebid Server Adapter (s2sConfig)" (accessed 9 November 2026). https://docs.prebid.org/dev-docs/modules/prebidServer.html
- Prebid.org, "Prebid Server Feature Index" (accessed 9 November 2026). https://docs.prebid.org/prebid-server/features/pbs-feature-idx.html
- Prebid.org, "Hosting a Prebid Server Cluster" (accessed 9 November 2026). https://docs.prebid.org/prebid-server/hosting/pbs-hosting.html
More sources (4)
- 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 - 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 - 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
- IETF, RFC 7386, "JSON Merge Patch" (October 2014). https://www.rfc-editor.org/rfc/rfc7386.html