# CSFolder Data API > Development preview. Read the current OpenAPI before generating an integration. Never invent endpoints or unavailable data. ## Build with AI Source: https://docs.csfolder.com/en/build-with-ai/ ## A prompt for your first product After documentation is published, give your agent this prompt. Before publication, replace the production documentation origin with your local documentation URL. ```text Build a CS2 price comparison web app using the CSFolder Data API. First read https://docs.csfolder.com/llms.txt and /openapi.json. Read /downloads/manifest.json; download the SDK from its same-origin URL, verify its SHA-256 and size, then install the local archive with --ignore-scripts. Use only operations and fields from the current contract. Implement item search, marketplace ask/bid tables and a daily chart. Read CSFOLDER_API_KEY only on the server. Never ask me to paste the key into chat or send it to the browser. Preserve item_id, phase, wear, StatTrak and Souvenir identity. Display currency, units, source and available data timestamps. Never invent missing prices, chart points or executed sales. Add pagination, a 30-minute cache, timeouts and bounded 429/503 retries. Test valid results, empty history, 401, 429 and 503 responses. If a required method is absent, explain the limitation; do not invent it. Do not deploy or connect paid services without my approval. ``` ## What “one prompt” means For a full-catalog product, ask the agent to read [full market snapshots](/en/market-snapshots/), consume NDJSON line by line into staging storage, and validate the matching `end` record before replacing the current dataset. Do not request every item's price separately or assume HTTP 200 proves a complete export. The agent receives a contract, examples and acceptance checks to scaffold a supported integration. The owner still authenticates, provisions a key and consents to private-data access. This does not guarantee every imaginable product or access to someone else's portfolio. ## Agent-readable formats For Node.js server integrations, use the [TypeScript SDK](/en/typescript-sdk/) when its local artifact is available. It shares the runtime contract, validates responses and verifies snapshot completion. It is not yet an npm release; do not invent an installation command for a public package. - [Downloads manifest](/downloads/manifest.json) — exact SDK/MCP archives, checksums and contract digest. - [OpenAPI](/openapi.json) — operations, input bounds and authentication. - [llms.txt](/llms.txt) — documentation map. - [llms-full.txt](/llms-full.txt) — all English guides in one file. - [This guide as Markdown](/markdown/en/build-with-ai.md) — without navigation or client JavaScript. The locally verified [MCP adapter and installable skill](/en/mcp-and-skill/) give agents bounded tools, the bundled contract and guided integration recipes. Use the supplied archive; a public npm release and hosted MCP endpoint are not available yet. `llms.txt` makes documentation easier to read; it does not guarantee AI-search indexing or recommendations. Do not install packages invented by an agent. --- ## Data quality and semantics Source: https://docs.csfolder.com/en/data-quality/ ## Prices are not sales `asks` are seller offers. `bids` are buy orders. Their spread is not guaranteed profit: fees, purchase/withdrawal availability, trade locks and liquidity matter. Daily price history is not a feed of completed transactions. ## Units Quotes use integer USD cents: `amount: 12345`, `currency: USD`, `minor_units: 2` means USD 123.45. Large capitalization values and decimal index/FX values are strings to preserve precision. Do not implicitly convert large monetary values to JavaScript Number. `rate_per_usd` means units of the target currency per 1 USD. `steam` and `cbr` rates are not interchangeable. ## Time and missing data - `generated_at` is the API response generation time. - `as_of` is the source observation time when known. - `as_of: null` means unknown; never substitute the current time. - `cache_recently_checked` describes the cache, not a new trade or marketplace quote. - Empty history means no available observations, not a zero price. The local pipeline supports 30-minute shared-feed refreshes and the first verified cached frame per UTC hour. [Hourly history](/en/hourly-history/) reads those frames; it does not promise all-game catalog or historical coverage. `checked_at` is cache verification, `ingested_at` mirror publication, and `source_observed_at` remains null without actual source timestamps. The daily `/history` remains separate. Never fabricate hourly observations by repeating daily values. ## Identity Do not merge items by name alone: Doppler phases can share `market_hash_name`. Use `item_id` and `phase`. Indexes retain methodology and membership versions; verify compatibility before combining versions into one continuous series. ## History windows Daily/index/ROI/FX history requests span at most 366 calendar days; hourly market history spans at most 31. Split longer periods into sequential windows. When `meta.next_page` is present, continue using `after` and unchanged filters. For catalog pagination, pass `meta.next_cursor` as `cursor`. Do not retry empty windows indefinitely. --- ## Hourly price history Source: https://docs.csfolder.com/en/hourly-history/ Implemented in the local API. Production deployment and historical coverage are separate release checks; do not assume that every item already has a full series. ## Read a chart series `GET /v1/items/{item_id}/history/hourly` reads our persisted hourly frames, never the upstream service. Resolve the exact item ID first; phases and wear are distinct. ```sh curl 'https://api.csfolder.com/v1/items/123/history/hourly?from=2026-09-01&to=2026-09-07&provider=steam&side=ask&limit=100' \ -H "Authorization: Bearer $CSFOLDER_API_KEY" ``` `123` is a placeholder. Keep credentials in the server environment, not prompts, URLs or browser code. See [keys and access](/en/keys-and-access/). - `side`: `ask` by default, or `bid`; there is no fallback between them. - `provider`: optional exact market slug. Omit it for every quoted market on the selected side. A selected missing provider never falls back to another market. - `from` / `to`: inclusive UTC calendar dates, at most **31 days**. Default: today and the preceding 30 days. Split all-time retrieval into bounded windows. - `limit`: rows, not hours; default 500, maximum 1,000. MCP defaults to 25/max 100. - `after`: use `meta.next_page` until null. Keep item, side, provider and dates unchanged. The cursor remembers omitted selection filters and resolved dates. Each row contains `ts`, `provider`, `side`, `amount`, `quantity`, `currency: USD`, `minor_units: 2`, `kind: hourly_cached_quote`, `availability`, `ingested_at`, `checked_at`, and `source_observed_at`. Amounts are integer USD cents; unknown quantities are null. Rows sort by UTC bucket and provider, with missing-provider markers first. The route remains available for identities with stored history even if they have been removed from the current catalog. ## Missing is not zero | `availability` | Meaning | | --- | --- | | `quoted` | Stored price exists for this provider and side. | | `item_missing` | A frame was recorded, but this exact item/phase was absent from its feed. Amount is null. | | `no_quote` | Item was present, but there was no quote for the selection. Amount is null. | Without a provider filter, available providers are returned individually; if none exist on the selected side, one null-provider marker is returned. Omitted providers are not individually enumerated as missing. With a provider filter, missing rows retain the requested provider name. An hour without a recorded frame has **no row**, not a zero or a copied prior price. Keep gaps visible in charts. Empty history is not proof that an item was worthless. ## What the clocks mean `ts` is the hourly bucket. `checked_at` is our successful GET/304 verification time; `ingested_at` is mirror publication time. `source_observed_at` is null when the feed does not provide a real quote timestamp. A 304 can confirm unchanged cached prices without representing a new marketplace tick. These are neither sales nor OHLC. Only the first successful verified frame per UTC hour is saved. Later refreshes within that hour do not overwrite it. Frames are immutable, but pagination is not a frozen export: a late insertion may require rereading the affected window and deduplicating by item ID, `ts`, provider and side. Daily history at `/history` remains a separate dataset; it is not expanded into hourly observations. ## Build with AI The SDK uses `api.request('getHourlyPriceHistory', { params, query })` and bounded `api.pages('getHourlyPriceHistory', input, { maxPages })`. The MCP tool has the same name and argument structure, fetching one page per call. Inspect `next_page` even when a page budget is reached: remaining data has not been downloaded. Ask your agent: “Build a Steam ask chart for this exact item using getHourlyPriceHistory. Preserve null prices and missing hours, show cache check time separately from source time, keep the key server-side and test pagination and 429.” See [TypeScript SDK](/en/typescript-sdk/) and [MCP and skill](/en/mcp-and-skill/). --- ## CS2 data. From idea to integration. Source: https://docs.csfolder.com/en/ :::caution[In development] This is a testable development contract, not a public launch announcement. Key provisioning, production access and plans are still being prepared. Never paste real API keys into prompts. ::: ## Start here - [AI quickstart](/en/build-with-ai/) — a ready-to-use integration prompt. - [First request](/en/quickstart/) — server-side authentication and data access. - [Data quality](/en/data-quality/) — price semantics, available history and missing data. - [Recipe: compare markets](/en/recipes/market-comparison/) — an end-to-end scenario with acceptance checks. ## Connected datasets The catalog connects items by `item_id`, retaining paint phase, wear, StatTrak and Souvenir identity. Observed prices and daily history attach to those items. Indexes and exchange rates retain their own timestamps and methodology. Read-only [personal portfolio methods](/en/portfolios/), [container ROI and history](/en/openable-roi/) and the [MCP adapter with agent skill](/en/mcp-and-skill/) are implemented and tested locally. Public rollout is still pending. The current method list is in [OpenAPI](/openapi.json). --- ## Keys and account access Source: https://docs.csfolder.com/en/keys-and-access/ :::caution[Development preview] The developer dashboard is implemented and tested locally. Production availability has not been verified. These instructions apply when Data API access is enabled on your CSFolder instance. ::: ## Create a key Open **Developer API** from the CSFolder account menu (`/developers`). The dashboard shows your current plan, monthly usage, request-per-minute ceiling and UTC reset date. All keys on that account share the same allowance. 1. Give the key a name that identifies your app. 2. Choose a project key for market data, or a personal key for selected portfolios. 3. Select an expiry of 7, 30 or 90 days. Personal keys also require explicit portfolio selection and consent. 4. Save the secret as the server-side `CSFOLDER_API_KEY` environment variable. It is shown only once and hidden after five minutes or when you leave the tab. It cannot be recovered from the key list. Creating or rotating credentials requires a sign-in within the last 30 minutes. If prompted, verify your sign-in and check the active CSFolder account. A copied documentation URL does not grant API access; requests need the bearer key. ## Rotate or revoke **Rotate** replaces the secret immediately and preserves its permissions and original expiry. The old secret stops working; update your app's environment variable with the new secret. There is no overlap period. **Revoke** permanently disables that key. It remains available from an older but still valid site session, so you do not have to reauthenticate just to remove access. Never paste the old or new secret into a support message or AI prompt. The recent-activity list records key creation, rotation, revocation and changes to account access. It never contains secrets. The account ID shown in the dashboard can be used for support; it is not an authentication credential. ## Plans and private data The dashboard displays the allowance currently assigned to you. Manual entitlement changes do not charge a payment or create a subscription, and do not reset usage already consumed this month. Public prices and billing availability remain unannounced during development. Personal keys read only the portfolios explicitly selected at issuance, and each request checks current ownership. A project key cannot read portfolios. Publicly sharing a portfolio on the website does not grant an API key permission to access it. Continue with [your first request](/en/quickstart/) or [personal portfolio access](/en/portfolios/). --- ## Limits and errors Source: https://docs.csfolder.com/en/limits-and-errors/ Allowance is shared by the account: creating more keys does not increase a plan. The approved launch plans below are implemented; purchases stay unavailable until production payment verification is complete. | Plan | Price / 30 days | Requests / UTC calendar month | Requests / minute | | --- | --- | --- | --- | | Free | $0 | 1,000 | 20 | | Builder | $20 | 10,000 | 60 | | Trader | $50 | 100,000 | 180 | Free uses real data, not a sandbox: item metadata without current quotes, plus Steam daily ask snapshots for the last 30 completed UTC days. `GET /v1/items/{item_id}/history` selects this window and Steam automatically. Other markets, today's prices, hourly history, market comparisons, full exports, indexes, ROI and exchange rates require a paid plan. Missing observations stay missing. Daily resolution is not a guarantee that every item has a new observation each day. Owner-scoped portfolio access remains available with an explicitly consented personal token; it does not include a live market-price feed. Both paid plans can read the available market datasets; they differ in quota, RPM, key and export ceilings, not source freshness. Neither plan promises a real-time sniper feed. After credential, request and plan validation, each admitted protected data-access attempt consumes one request, including a read failure. Rejected rate-limit or plan checks do not consume another unit. A batch of up to 100 items counts as one request. Crypto purchases are prepaid for 30 days, without automatic renewal. A purchase or renewal does not reset used calendar-month quota. Headers: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-Quota-Remaining`, `Retry-After`, `X-Request-Id`. Monthly usage is recorded durably before execution; a cache restart does not reset it. On minute-limit rejections, `X-Quota-Remaining` can be absent because the monthly balance was not read. A monthly denial can still consume a minute attempt; it does not consume another monthly unit. | HTTP | Client action | | --- | --- | | 400 | Correct parameters, range or cursor. Do not retry unchanged. | | 401 | Check key validity and expiry. Never send the key to support. | | 403 | Check the error code: `plan_upgrade_required` needs a paid plan; `insufficient_scope` needs an appropriate key scope. | | 404 | Item or endpoint does not exist. | | 413 | Reduce the request body. | | 429 | Follow `Retry-After`; do not bypass quotas with extra keys. | | 503 | Retry a bounded number of times with backoff; display unavailability. | Retries need an overall timeout, an attempt cap and jitter. If the monthly allowance is exhausted, do not leave the application in an infinite retry loop. [Hourly market history](/en/hourly-history/) is limited to 31 inclusive UTC days and 1,000 rows per request. Invalid ranges or mismatched cursors return 400 before monthly quota is consumed. A successful empty window or a dependency failure after admission still consumes one request. [Full market snapshots](/en/market-snapshots/) have separate account-wide concurrency and rolling 24-hour start limits. A response can fail after HTTP 200: require a valid matching `end` record before accepting the export. --- ## Full market snapshots Source: https://docs.csfolder.com/en/market-snapshots/ Development preview: this method is implemented locally, not yet a verified public service. `POST /v1/markets/snapshot` streams the current cached market matrix as `application/x-ndjson`: one JSON record per line. It requires `market:read`. Send only the Authorization header, with no body or query parameters. ```sh curl --no-buffer --fail-with-body -X POST \ -H "Authorization: Bearer $CSFOLDER_API_KEY" \ https://api.csfolder.com/v1/markets/snapshot ``` This reads our database and cache. It does not fetch upstream data for each customer. It covers the catalog stored in CSFolder, not a promise that every item in the game has a quote on every marketplace. History is a separate dataset. ## Completion protocol | Record | Meaning | | --- | --- | | `start` | Request ID, price-cache generation, ingest time and upper catalog item ID. | | `item` | `data.item_id`, `market_hash_name`, exact `phase`, `availability`, `asks` and `bids`. | | `end` | Matching request ID and generation, plus the number of item records sent. | | `error` | The export failed. Discard the partial dataset. No successful `end` follows. | Each quote has integer USD-cent `amount`, `currency: USD`, `minor_units: 2`, provider, quantity and URL. Quantity and URL can be null. Missing cache entries use `availability: missing` and empty quote arrays, not a zero price. Bid prices never substitute for asks; phases never fall back to an unphased item. HTTP 200 alone is **not** success. Require exactly one `start`, ordered `item` records and one matching `end`; verify the item count and reject unknown, extra, malformed or truncated records. A network interruption may have no `error` line. Read line by line rather than calling `response.json()` or accumulating the whole body. The maximum encoded record is 256 KiB. Write into a temporary file or staging table. Only promote it after the complete protocol has been validated. On `snapshot_changed` or `snapshot_interrupted`, discard the staging data and retry the entire request with bounded backoff. There is no resume cursor in this version. Interrupted starts can still spend quota, so do not retry forever. ## Consistency and timestamps A successful export contains prices from one cache generation. An update during streaming invalidates the export. Catalog identities are scanned in increasing item ID up to the captured maximum; this is not a point-in-time database snapshot. `ingested_at` is when CSFolder published that cache generation. `generated_at` is when the export started. Neither is the marketplace observation time: the current feed does not supply it, so `price_as_of` and quote `as_of` remain null. A recent export can contain older source prices. Do not turn an export timestamp into an observed hourly history point without preserving this distinction. ## Export limits One active export per account, shared across keys. There are eight active export slots across the service and a two-minute export deadline. Slow clients are backpressured; disconnected clients stop further reads. Schedule with jitter and honor `Retry-After` rather than starting every customer at the same second. Development defaults for starts in a rolling 24-hour window: | Plan | Starts | | --- | ---: | | Free | Not available (403 before quota or lease) | | Builder | 48 | | Trader | 144 | | Quant | 288 | These are draft access settings, not published subscriptions. Each admitted export also costs one monthly request. Slot/daily-limit rejections occur before monthly charging. Failure before streaming releases the daily reservation, but an already admitted monthly request is not refunded. Once streaming starts, client cancellation or a later failure still counts as a start. A crashed process may retain its reservation until expiry. HTTP 429 uses `snapshot_busy`, `snapshot_capacity` or `snapshot_daily_limit` and a `Retry-After` header. Before response headers, missing data can return HTTP 503. After streaming starts, follow the completion protocol above. See [limits and errors](/en/limits-and-errors/). --- ## MCP and agent skill Source: https://docs.csfolder.com/en/mcp-and-skill/ ## Availability The stdio MCP adapter and installable skill are implemented and tested locally. They are not yet public npm releases, and there is no hosted MCP/OAuth endpoint. Do not point an MCP client at the REST origin or invent an `npx` package name. Production API access is a separate release step. ## Install the supplied artifact Requirements: Node.js 22+ and an MCP host that supports stdio. Install the verified archive from [downloads](/downloads/). Read the [manifest](/downloads/manifest.json), choose `@csfolder/mcp`, and verify the downloaded file's SHA-256 and size before installing. Downloads become public with this documentation deployment. The checksum is not a signed release; use only the trusted documentation origin. ```sh npm install --ignore-scripts /absolute/path/csfolder-mcp.tgz ``` Maintainers can generate that archive with `pnpm --filter @csfolder/mcp pack --out /absolute/output/csfolder-mcp.tgz`. This installs the local file, not a similarly named registry package. The MCP entrypoint is `node_modules/@csfolder/mcp/dist/stdio.js`. ## Connect Codex After explicitly choosing this integration, add this non-secret configuration to the project's `.codex/config.toml` or your existing Codex configuration. Replace both paths with actual absolute paths; preserve other settings. ```toml [mcp_servers.csfolder] command = "/absolute/path/to/node" args = ["/absolute/path/to/project/node_modules/@csfolder/mcp/dist/stdio.js"] env_vars = ["CSFOLDER_API_KEY"] startup_timeout_sec = 15 tool_timeout_sec = 30 ``` Set `CSFOLDER_API_KEY` securely in the host environment before starting/restarting the MCP connection. `env_vars` forwards its value without putting the secret in this configuration. A project's `.env` is not automatically loaded by the MCP executable. Never paste the key into chat or CLI arguments. This uses Codex's [documented stdio environment forwarding](https://developers.openai.com/codex/mcp). Other clients need their equivalent secure environment configuration. Without a credential, discovery, prompts, resources and `getStatus` still work; protected tools return `invalid_api_key`. ## Try a bounded workflow Ask the agent: ```text Read csfolder://docs/integration and csfolder://openapi. Build my CS2 marketplace price comparison in the existing project. Use listItems to resolve exact item IDs, then getMarketPrices. Keep missing prices, unknown timestamps and asks/bids distinct. Use a server-side SDK route for the app, never expose the API key. Test empty data, pagination, 401 and 429. Do not deploy yet. ``` The MCP prompt `build_with_csfolder` also accepts `recipe`: `market-comparison`, `openable-roi` or `portfolio`, plus optional `locale`: `en` (default) or `ru`. Tool names match the JSON operation IDs. Example arguments: ```json {"query":{"q":"AK-47 | Redline","limit":5}} ``` Pass that object to `listItems`, then use an actual returned `item_id` as `{"params":{"item_id":123}}` for `getMarketPrices`; `123` is only a placeholder. ## Private portfolios require a separate choice By default, only 17 market/status tools are visible. To enable the five portfolio tools, the owner must choose a personal `csf_pat_` credential with access to selected portfolios and set `CSFOLDER_MCP_PORTFOLIOS=1` in the MCP process environment. Retrieved private data enters the connected AI host/model. Enabling the tools does not grant permission: the API still checks scopes, ownership, revocation and expiry. ## Install the skill The package contains `skill/csfolder-api/`. Copy the whole directory from `node_modules/@csfolder/mcp/skill/csfolder-api/` into the consuming project's `.agents/skills/csfolder-api/`, or the host's supported skill directory. Inspect an existing destination before replacing it. No global configuration changes are needed. Then ask: `Use $csfolder-api to build my CS2 data integration in this project.` The skill can use MCP, a local OpenAPI artifact, or accessible documentation. It does not issue a key, consent to private access, or authorize deployment for you. ## Budgets and large datasets Each tool makes at most one HTTP request: no implicit retries or automatic next page. Default page size is 25, maximum 100; at most four calls run concurrently. The default deadline is 15 seconds. Oversized responses fail explicitly (128 KiB HTTP input, 256 KiB complete tool result), rather than returning a misleading partial result. Preserve cursors and use bounded date windows. Honor quota errors. For the complete market matrix, use the [SDK](/en/typescript-sdk/) and [streaming snapshot guide](/en/market-snapshots/) in a backend job. MCP deliberately does not place a full export in model context, write files or execute shell commands. Protocol and synthetic-data integration tests prove tool interoperability, not that any arbitrary product can be completed by one prompt or that production has already been deployed. --- ## Container ROI and history Source: https://docs.csfolder.com/en/openable-roi/ :::caution[Development contract] These methods are implemented and tested locally. Public production access has not launched. They read CSFolder's existing calculations; they never trigger an upstream request or calculate a probability tree per API call. ::: ## Three methods - `GET /v1/openables?valuation_mode=steam_gross&limit=100` — paginate the public catalog, not a top-100 subset. Pass `meta.next_page` as `after` until it is null. - `GET /v1/openables/{slug}?valuation_mode=cash_ask` — current published calculation and quality fields for the exact slug returned by the catalog. - `GET /v1/openables/{slug}/history?from=2026-08-01&to=2026-08-31&granularity=day` — stored model snapshots. Pagination uses `after`; keep all filters unchanged. All three require a Bearer key with `market:read` and use the same account quota as other market methods. Inactive containers are hidden, except retired Armory pools retained with `active: false` for hypothetical analysis. Draft-only definitions and draft calculations are not published. ## Valuation and units `valuation_mode` accepts `steam_gross` (default) or `cash_ask`. These are valuation models, not selectable individual marketplaces or guaranteed liquidation proceeds. There is no silent fallback from a missing cash calculation to Steam, or vice versa. `current: null` means no published calculation for the selected mode and current methodology, not zero ROI. Money fields ending in `_cents` use integer USD cents, with `currency: "USD"` and `minor_units: 2`. Decimal percentage fields are strings to preserve precision. | Field | Meaning | | --- | --- | | `cost_cents` | Total modeled cost of one opening/claim under the selected model | | `expected_value_cents` | Modeled expected output value, not the median or guaranteed sale value | | `expected_profit_cents` | Modeled expected value minus modeled cost | | `return_pct` | Expected value / cost × 100 | | `profit_roi_pct` | `return_pct` − 100, present on the current calculation | | `profit_chance_pct` | Modeled probability of a profitable outcome, not the return percentage | For example, a modeled cost of 1,000 cents and expected value of 800 cents mean `return_pct: "80.000000"`, `profit_roi_pct: "-20.000000"` and `expected_profit_cents: -200`. Return percentages are not probabilities: they can exceed 100, and profit ROI can be negative. These metrics do not promise realized profit or account for every user's execution conditions. ## Quality and time Missing point estimates remain `null`. The current calculation also returns lower/upper expected-value and profit-chance bounds, quantiles, price-coverage percentages, confidence and flags. Do not turn an unknown upper bound into zero or treat the lower bound as the point estimate. `priced_probability_mass_pct` describes coverage of possible outcomes, not the chance of profit. - `calculated_at` is the current model's calculation/check time. - `price_as_of` is the stored input-price time; it may be null and is not an individual timestamp for every outcome. - `stale` follows the site's ROI policy: calculation age over 8 hours, unknown price time or price age over 24 hours. - `meta.generated_at` is only response generation time. The worker currently recalculates ROI on a six-hour schedule. More frequent API calls do not make the model fresher. ## Historical model snapshots Select `granularity=hour|day` and optionally `methodology_version`. The default methodology matches the current calculation; specify the version explicitly to keep a backfill stable across API upgrades. Each row retains `revision_id`, `revision_number` and `methodology_version`; do not merge incompatible versions into one unqualified curve. Published revisions that were later retired remain readable. Pages read the current stored history, not an immutable point-in-time export: a later reconstruction may revise historical values. One request spans at most 366 UTC calendar days and returns up to 2,000 rows (default 1,000). The default window is the last 31 calendar days. For a longer backfill, request consecutive windows and follow each window's `meta.next_page` before advancing. History returns only the selected resolution: there is no hourly/daily fallback, interpolation or gap filling. An existing container without matching points returns an empty array. `kind: "model_snapshot"` distinguishes this from actual sales or realized investment returns. `ts` is the bucket timestamp; `recorded_at` is when the stored row was written, not when all input prices were observed. Existing storage does not distinguish live model snapshots from historical reconstructions, so `provenance: "not_recorded"` is explicit. An hourly bucket does not prove a new hourly calculation. Do not call this an independently observed hourly ROI series. ## Prompt for an ROI comparison tool ```text Build a CS2 container comparison dashboard using CSFolder Data API. Read /llms.txt and /openapi.json first. Keep CSFOLDER_API_KEY on the server. Load every catalog page from /v1/openables; do not stop at the first 100. Keep steam_gross and cash_ask separate. Show modeled cost, expected value, profit ROI, profit chance, coverage, confidence, price time and stale status. Treat all null estimates as unavailable. Never present expected ROI as a guarantee. Draw history from the documented history method without filling missing dates. Keep revision/methodology changes visible and label reconstruction provenance unknown. Test empty history, partial pricing, negative ROI, pagination and 401/429/503. ``` See [data quality](/en/data-quality/) and the generated [API reference](/reference/) for the current contract. --- ## Personal portfolio access Source: https://docs.csfolder.com/en/portfolios/ :::caution[Development preview] The methods below are implemented and tested locally. The production developer dashboard and self-service access are still being prepared. ::: ## Explicit permission Use a personal access token (`csf_pat_…`) with `portfolio:read`, not a project key (`csf_live_…`). The owner must select specific portfolios and consent to read-only access. A shared portfolio URL, Steam ID or portfolio ID does not grant this permission. Tokens are revocable, expire within 90 days, and are shown only at creation or rotation. Rotation invalidates the previous secret immediately without extending its expiry. Store the token in a server-side secret, never in prompts or browser code. ## Read the connected portfolio 1. `GET /v1/me/portfolios` lists only the token's selected portfolios that still belong to its owner. 2. `GET /v1/me/portfolios/{portfolio_id}` returns settings. 3. Add `/holdings` for inventory lots or `/transactions` for ledger rows. 4. Add `/history?from=2026-09-01&to=2026-09-28` for available daily valuation snapshots. List methods return `meta.next_page`; pass it unchanged as `after`. Keep the same filters while paginating. History and transactions accept windows up to 366 days, defaulting to the last 31 days. Unauthorized, unselected and nonexistent portfolio IDs all return `404` to an otherwise valid personal token. Project keys receive `403` on private methods. ## Keep financial meaning intact - Monetary fields are integer USD cents, even if `display_currency` is RUB. - A holding's `unit_cost_cents` is its per-item purchase basis, not its current market price. Null means unknown; `cost_estimated` remains explicit. - Transactions are this owner's stored ledger entries, not marketplace-wide executed sales. They are not a complete realized-profit calculation. - History retains the actual `price_source` on each point. Null identifies unknown legacy sources; never replace it with the portfolio's current provider. - Changes in total portfolio value are not investment returns: deposits, withdrawals and inventory changes can affect totals. Do not label these snapshots TWR or IRR. Notes, Steam account identifiers and external integration references are excluded from these API responses. All portfolio methods share the same account quota as market methods. --- ## First request Source: https://docs.csfolder.com/en/quickstart/ :::caution[Development preview] Examples target the current development contract. Production DNS and self-service keys are not yet verified. Use `http://127.0.0.1:3100` and a local test-account key for local testing. ::: ## Authentication Send credentials only in `Authorization: Bearer …`. Never put keys in URLs, Git, client-side JavaScript or agent messages. Keep the key in the server-side `CSFOLDER_API_KEY` environment variable. ```sh curl --fail-with-body \ -H "Authorization: Bearer $CSFOLDER_API_KEY" \ "http://127.0.0.1:3100/v1/items?q=AK-47&limit=10" ``` ## Search → prices → history On Free, search the catalog and call `GET /v1/items/{item_id}/history` without dates: it returns available Steam daily observations for the last 30 completed UTC days. Current quote fields in the catalog are null. The multi-market example below requires Builder or Trader; see [plans and limits](/en/limits-and-errors/). 1. Search with `GET /v1/items?q=…`. 2. Use a returned `item_id`; never guess IDs or merge paint phases. 3. Read `GET /v1/items/{item_id}/markets`. 4. Draw a chart from `GET /v1/items/{item_id}/history?provider=buff163&from=2026-08-01&to=2026-08-31`. History contains available daily observations, not a guarantee of every day for every item since release. Items, marketplaces and periods can have gaps. ## Server-side JavaScript ```js const key = process.env.CSFOLDER_API_KEY; if (!key) throw new Error('Set CSFOLDER_API_KEY on the server'); const response = await fetch('http://127.0.0.1:3100/v1/items?limit=10', { headers: { Authorization: `Bearer ${key}` }, signal: AbortSignal.timeout(10_000), }); if (!response.ok) throw new Error(`CSFolder returned ${response.status}`); const { data, meta } = await response.json(); console.log(data, meta.request_id); ``` Responses wrap method output in `data`. `meta.request_id` is a diagnostic identifier. `meta.generated_at` is the response timestamp, **not the price timestamp**. Errors use `application/problem+json`. --- ## Marketplace price comparison Source: https://docs.csfolder.com/en/recipes/market-comparison/ ## What to build Item search → seller offers and buy-order tables → a daily chart for a selected marketplace. Your server stores the key and caches public market data. The browser receives only the required fields. ## Request sequence 1. Debounced `GET /v1/items?q=…&limit=20`, not one request for every keystroke. 2. `GET /v1/items/{item_id}/markets` for the selected item. 3. `GET /v1/items/{item_id}/history` for one marketplace and a bounded date range. Sort `asks` by ascending `amount` and `bids` by descending `amount`. Keep them separate. Format money using `minor_units`. Do not equate Steam wallet balances with withdrawable cash balances. ## Acceptance checks - Search displays API results, not hard-coded demonstration prices. - Selecting another phase uses that phase's `item_id`. - Empty history has a clear empty state. - Charts do not contain points absent from the response. - 429 respects `Retry-After`; 401 stops further requests. - Keys do not appear in HTML, browser bundles, URLs or logs. - Repeated views use a shared cache; 100 viewers do not cause 100 upstream requests. This compares observed offers. It is not a trading bot or a promise of profit. ## Runnable starter Get **Market comparison starter** and the SDK from [Downloads](/downloads/). Their exact filenames and SHA-256 values are in [the manifest](/downloads/manifest.json) (`examples` and `artifacts`). Verify checksums, extract the starter into a new directory, install the local SDK archive with `npm install --ignore-scripts /path/to/archive.tgz`, set `CSFOLDER_API_KEY` in the server environment, then run `npm start`. Open `http://127.0.0.1:4319`. Node.js 22+ is required; follow the bundled README. This English reference UI uses real API responses, not bundled demo prices. It shows the last 30 UTC days of daily asks as individual dots with a data table. Search pagination, bid-only items, unknown timestamps and empty history are explicit. Quotes/history cache for 30 minutes; search caches for 30 seconds. 401/403 and exhausted monthly quota require an operator fix and server restart; 429 pauses requests until Retry-After, with explicit retry. No background polling. The app is **local-only**, not a publicly deployable unauthenticated proxy. Before hosting it for users, add app authentication, per-user abuse controls and shared caching/limits across replicas. Do not expose it through a tunnel as-is. The starter does not provision API access; check release availability first. --- ## TypeScript SDK Source: https://docs.csfolder.com/en/typescript-sdk/ The Node.js 22+ SDK is implemented and tested locally. It is **not published to npm yet**, and production API access is not generally available. Do not ask an agent to install an assumed public package. MCP and the installable agent skill are separate deliverables, not included in this SDK. ## Download and install The documentation build includes [SDK and MCP downloads](/downloads/) and a [machine-readable manifest](/downloads/manifest.json). These files become public when this documentation release is deployed; they are not an npm registry release. Choose `@csfolder/sdk` from the manifest, download its `url` from the same trusted documentation origin, and compare the file's SHA-256 and byte count with the manifest before installing it in your server project: ```sh npm install --ignore-scripts /absolute/path/to/downloaded-sdk.tgz ``` Archive URLs contain their content digest. Save the archive and your lockfile for reproducible installs: old URLs are not guaranteed to remain after a docs upgrade. The manifest also hashes the exact `/openapi.json` bytes. Checksums detect corruption, not a compromised origin; this is not a signed release manifest. Installation may fetch the package's pinned public runtime dependencies. ## Build from source (maintainers) From a trusted checkout of CSFolder: ```sh pnpm --filter @csfolder/sdk pack --out /absolute/path/csfolder-sdk-0.1.0.tgz ``` Packing builds and generates the client types from the current OpenAPI contract. Install that artifact in your application's **server** package: ```sh pnpm add /absolute/path/csfolder-sdk-0.1.0.tgz ``` ## First typed request Set `CSFOLDER_API_KEY` in your server environment through its secret manager. Never put the key in a prompt, URL, browser component or public environment variable. ```ts import { CSFolderClient } from '@csfolder/sdk'; const api = new CSFolderClient(); const { data: items } = await api.request('listItems', { query: { q: 'AK-47', limit: 10 }, }); if (items[0]) { const quotes = await api.request('getMarketPrices', { params: { item_id: items[0].item_id }, }); // quotes.data.asks / bids: integer USD cents, nullable source timestamps. } ``` JSON operations use `request(operationId, input)`. The SDK validates requests and responses against the shared contract; use [OpenAPI](/openapi.json) for available fields. It preserves missing values and decimal strings. [Private portfolios](/en/portfolios/) still require an owner-authorized personal token. The default origin is `https://api.csfolder.com`. For a local API, explicitly set `baseUrl: 'http://127.0.0.1:PORT'` and `allowLocalhost: true`. Alternate remote origins and redirects are rejected. ## Pagination with a budget ```ts for await (const page of api.pages( 'listItems', { query: { limit: 100 } }, { maxPages: 5 }, )) { // Process page.data; save page.meta.next_cursor if more data remains. } ``` `maxPages` is required (1–100). Reaching the budget does not mean the catalog is complete. Filters are preserved; repeated cursors are rejected. Each page has its own request deadline. Pass an `AbortSignal` to cancel traversal. The SDK has no shared data cache; private responses must never be cached across credentials. ## Full market snapshots Use `api.streamMarketSnapshot({ signal })` for [full-market exports](/en/market-snapshots/). It yields typed `start`, `item`, and `end` records. Process rows into staging storage; promote the dataset **only after the loop completes successfully**. An early `break`, abort or exception requires discarding staging data. The client verifies schema, UTF-8, ascending IDs, generation/request ID/count, a 256 KiB record limit and clean EOF. It emits `end` only after EOF. Streaming is not retried automatically. The default total deadline is 120 seconds, including consumer processing; `timeoutMs` can lower it. ## Errors and request limits JSON requests default to 15 seconds and an 8 MiB decoded response limit. Retries are disabled by default; `maxRetries: 1` or `2` permits bounded 429/503 retries that respect `Retry-After` and the remaining deadline. Monthly quota exhaustion is never retried automatically. Each retry may consume quota. Catch `CSFolderError` and inspect `code`, `status`, `requestId` and `retryAfterSeconds`. Messages are sanitized and do not repeat raw server details. See [limits and errors](/en/limits-and-errors/). For an AI-generated project, ask it to use this SDK on the server, preserve [data semantics](/en/data-quality/), and test empty history, access denial, quota exhaustion and interrupted exports. A generated interface does not create missing historical data or authorize deployment.