Get started
- 1Activate BMarker Pro.
The report endpoint is available to workspaces with an active Pro plan.
- 2Create an API key.
Open Settings → Report API keys, name the key, and copy its secret.
- 3Send the key with each request.
Store it with your application secrets and use the Bearer header below.
Authorization: Bearer bmk_live_your_keyRequest and HTML example
This request produced the report shown beside it. The checked-in HTML was generated from the same shared report component as the API's HTML and PDF output.
{
"analysis": {
"analysisWindow": null,
"balanceNormalization": "enabled",
"regressionWindow": null,
"rollingWindow": 126
},
"branding": {
"accentColor": "#0c6748",
"logoDataUrl": null,
"name": "Example Capital",
"showBmarker": false,
"websiteUrl": "https://example.com"
},
"format": "html",
"portfolio": {
"benchmarkSymbol": "SPY",
"currency": "USD",
"name": "Long-term portfolio"
},
"presentation": {
"balancePrivacy": true,
"shareTransactionNotes": false,
"showHoldings": false,
"showTrades": false
},
"title": "Portfolio performance report",
"transactions": [
{
"date": "2026-01-05",
"fees": "1",
"notes": "Opened the position",
"price": "185",
"quantity": "10",
"symbol": "AAPL",
"type": "BUY"
},
{
"date": "2026-03-02",
"fees": "0.5",
"notes": "Trimmed the position",
"price": "201.4",
"quantity": "2",
"symbol": "AAPL",
"type": "SELL"
}
]
}curl --request POST https://bmarker.com/api/v1/reports \
--header "Authorization: Bearer $BMARKER_API_KEY" \
--header "Content-Type: application/json" \
--data @report.json \
--output portfolio-report.htmlChange format and the output filename to create PDF, XML, or JSON.
Request fields
Send UTF-8 JSON with Content-Type: application/json. Prices, quantities, and fees accept JSON numbers or decimal strings.
Top-level fields
| Field | Required | Description |
|---|---|---|
format | Yes | html, pdf, xml, or json. |
portfolio | Yes | The portfolio name, currency, and benchmark ticker. |
transactions | Yes | Between 1 and 2,000 BUY or SELL records. |
analysis | No | Analysis, regression, rolling, and balance-normalization settings. |
presentation | No | Balance privacy, holdings, trades, and transaction-note settings. |
title | No | Up to 140 characters. Defaults to “Portfolio performance report.” |
branding | No | A display name, accent color, and website link. |
Portfolio and analysis
| Field | Accepted values |
|---|---|
portfolio.name | 1–100 characters. |
portfolio.currency | A three-letter code. Defaults to USD. |
portfolio.benchmarkSymbol | A ticker up to 20 characters. Defaults to SPY. |
analysis.analysisWindow | null, 21, 63, 126, or 252. Null uses all available history. |
analysis.regressionWindow | null, 21, 63, 126, or 252. Null uses the full analysis span. |
analysis.rollingWindow | 21, 63, 126, or 252. Defaults to 126. |
analysis.balanceNormalization | enabled or disabled. Defaults to enabled. |
Presentation
These fields are passed to the shared report component used by public share pages. The defaults match a new share page.
| Field | Default | Use |
|---|---|---|
presentation.balancePrivacy | true | Shows return, benchmark, excess-return, and drawdown cards. Set to false to show current value, net invested, gain/loss, and portfolio return. |
presentation.showHoldings | false | Adds current holdings and holdings-over-time sections. |
presentation.showTrades | false | Adds the trade-activity section. |
presentation.shareTransactionNotes | false | Includes transaction notes when trade activity is shown. |
Branding
Branding appears in the HTML and PDF header and is included in the XML and JSON report envelope. When branding is omitted, the report uses “Portfolio report” and BMarker green.
| Field | Default | Use |
|---|---|---|
branding.name | Portfolio report | A 1–100 character brand name shown in the report header. |
branding.websiteUrl | null | An HTTP or HTTPS link for the brand name and logo. Credentials in the URL are rejected. |
branding.accentColor | #0c6748 | A six-digit hex color used for report accents and positive values. |
branding.logoDataUrl | null | A base64 PNG, JPEG, or WebP data URL up to 350,000 characters. The header uses the first letter of the brand name when this is null. |
branding.showBmarker | false | Set to true to add the “Powered by BMarker” footer. |
A logo value begins with a media prefix such asdata:image/png;base64,, followed by the image's base64 data. PNG, JPEG, and WebP signatures are checked before the report is generated.
Transaction fields
| Field | Required | Description |
|---|---|---|
date | Yes | A real calendar date in YYYY-MM-DD form. |
type | Yes | BUY or SELL. |
symbol | Yes | A ticker up to 20 characters. |
quantity | Yes | A decimal value greater than zero. |
price | Yes | The execution price, greater than zero. |
fees | No | A decimal value of zero or greater. Defaults to zero. |
notes | No | Up to 500 characters, or null. |
Report data
BMarker loads completed daily closes, applies stock splits, repairs missing close ranges, and runs the same transaction-aware performance engine used by the app. QuantStats supplies the deeper risk and benchmark-relative analytics.
| Field | Contents |
|---|---|
performance | Summary, timeframe returns, holdings, daily series, cumulative growth, excess return, alpha, balance, and holdings history. |
analytics | Sharpe, Sortino, CAGR, volatility, drawdowns, capture, correlation, calendar returns, distributions, and rolling series. |
intraday | The latest Pro price snapshot and current portfolio valuation, including quote freshness and any fallback symbols. |
methodology | The calculation rules and engine version used for the report. |
effectiveWindows | The analysis and regression windows supported by the available observations. |
excludedSymbols | Symbols that could not be analyzed, with reasons and affected transaction row IDs. |
transactions | The normalized transaction rows used by the analysis. |
Historical returns and risk metrics have a completed-close timestamp inperformance.summary.asOf. Current valuation comes fromintraday.valuation, with quote details in intraday.snapshot. HTML and PDF show that value whenpresentation.balancePrivacy is false.
Formats
A successful response has status 200 and a filename in theContent-Disposition header, such aslong-term-portfolio-performance.pdf.
| Format | Content-Type | Contents |
|---|---|---|
html | text/html | A self-contained share-page report with embedded styles. |
pdf | application/pdf | A print rendering of the same HTML report. |
xml | application/xml | The complete report envelope as XML. |
json | application/json | The complete report envelope as JSON. |
Errors
Error responses use JSON. Every response has an X-Request-ID header; error bodies repeat it as requestId for support requests.
{
"error": {
"code": "invalid_request",
"message": "transactions.0.date: Use a real calendar date."
},
"requestId": "87a20465-1b42-4d85-9305-55467b783064"
}| Status | Meaning |
|---|---|
400 | The JSON or one of its fields is invalid. |
401 | The API key is missing, malformed, deleted, or unknown. |
403 | The key lacks report permission or the workspace needs an active Pro plan. |
413 | The request body exceeds 2 MiB. |
415 | The Content-Type header must be application/json. |
422 | The transactions could not produce an analyzable portfolio report. The body includes the performance issues. |
429 | The market-data provider is rate limited. |
503 | Market data, analytics, or the report renderer is temporarily unavailable. |
504 | Analytics or PDF rendering timed out. |
API keys
The full key appears once when it is created. Give each integration its own key so its access can be managed independently.
Roll creates a replacement while the current key stays active. Update the integration, confirm a successful request, and delete the previous key. Deletion takes effect immediately.
Manage API keys →Limits
- 1–2,000 transactions per report.
- Up to 50 unique transaction symbols.
- 2 MiB maximum request body.
- Up to 12 decimal places in decimal-string inputs.
- Submitted transactions remain in process memory while the report is generated.
- Completed daily market prices use BMarker's shared market-data cache.
Questions? Email support@bytescale.io.