One stateless request

Optimize a series of tickers without a browser session

The public endpoint accepts JSON directly from agents, scripts, and services. It requires no API key, CSRF token, cookie, or prior session setup. All returns are decimal values, so 0.12 means 12%.

Method
POST
Endpoint
/api/v1/optimize
Content type
application/json
Default limit
6 requests / 60 seconds / IP

01

Quick start

Provide two or more unique symbols. The options object is optional.

cURL Public · no authentication
curl --request POST "https://crafter.dlauinger.com/api/v1/optimize" \
  --header "Content-Type: application/json" \
  --data '{
    "tickers": ["AAPL", "MSFT", "BND"],
    "options": {
      "risk_metric": "standard_deviation",
      "num_years": 10,
      "num_portfolios": 10
    }
  }'
Discovery: Use GET /api/v1 for endpoint discovery or fetch the versioned OpenAPI 3.1 contract.

02

Request contract

Unknown fields are rejected at both the root and options levels.

Public optimizer request fields
Field Type Required Rules
tickers string[] Yes 2–20 unique symbols. Symbols are trimmed, validated, and normalized to uppercase; each is at most 15 characters.
options object No May contain only the three fields below.
options.risk_metric string No standard_deviation (default) or downside_deviation.
options.num_years integer No 5–20 years; default 10.
options.num_portfolios integer No 2–100 sampled portfolios; default 10.
Minimal JSON body
{
  "tickers": ["AAPL", "MSFT"]
}

03

Response contract

A successful response echoes the normalized request and places optimizer output in data.

Fields agents usually need

data.discrete_results
Requested number of sampled, risk-minimized frontier portfolios.
data.efficient_frontier
The complete generated frontier.
data.best_sampled_ratio_portfolio
Best defined Sharpe or Sortino ratio among sampled frontier points; may be null.
data.tickers and data.missing_tickers
Symbols included in the common data window and any requested symbols that could not be used. Every portfolio weights array follows the exact order in data.tickers.
data.*_basis
Machine-readable provenance for returns, risk, and ratios.
Success response · abbreviated values
{
  "api_version": "v1",
  "request": {
    "tickers": ["AAPL", "MSFT", "BND"],
    "options": {
      "risk_metric": "standard_deviation",
      "num_years": 10,
      "num_portfolios": 10
    }
  },
  "data": {
    "tickers": ["AAPL", "MSFT", "BND"],
    "start_date": "2016-08-29",
    "end_date": "2026-08-28",
    "discrete_results": [
      {
        "portfolio_cagr": 0.124,
        "optimization_return": 0.119,
        "portfolio_risk": 0.168,
        "sharpe_ratio": 0.71,
        "sharpe_ratio_status": "defined",
        "weights": [0.42, 0.38, 0.20],
        "feasible": true
      }
    ],
    "best_sampled_ratio_portfolio": {
      "weights": [0.42, 0.38, 0.20],
      "sharpe_ratio": 0.71
    },
    "ratio_selection_status": "defined",
    "optimization_return_basis": "weighted_asset_cagr",
    "portfolio_cagr_basis": "compounded_daily_returns",
    "optimization_risk_basis": "annualized_covariance",
    "reported_risk_basis": "annualized_standard_deviation"
  }
}

The example is deliberately abbreviated; production responses also include ticker statistics, a correlation matrix, date-range detail, the full frontier, ratio bases, and the risk-free rate.

04

Metric bases are intentionally explicit

Do not substitute similarly named fields. Some metrics differ by design because their series and optimization goals differ.

Optimization return

optimization_return is the weighted asset CAGR used by the solver's target-return constraint. Its basis is weighted_asset_cagr.

Portfolio CAGR

portfolio_cagr compounds the combined weighted daily portfolio series. Its basis is compounded_daily_returns, so it can differ from optimization return.

Standard-deviation risk

The standard frontier minimizes annualized_covariance; reported volatility uses annualized_standard_deviation.

Downside risk

The downside frontier minimizes centered_zero_clipped_covariance. Reported downside deviation is conditional loss-period RMS severity. These are intentionally different.

Sharpe ratio

Annualized arithmetic return minus annual risk-free rate, divided by annualized volatility.

Sortino ratio

Annualized mean daily excess over the daily-equivalent risk-free target, divided by all-period lower-partial-moment shortfall deviation.

Null is meaningful. A ratio may be null when its denominator is zero or otherwise undefined. Check its companion *_status field instead of coercing null to zero.

05

Errors and rate limits

Every public API error uses one stable envelope.

Error envelope
{
  "api_version": "v1",
  "error": {
    "code": "validation_error",
    "message": "tickers must contain at least 2 unique ticker symbols"
  }
}
Public API error codes
HTTP error.code
400validation_error
404not_found
405method_not_allowed
413payload_too_large
422optimization_failed
429rate_limit_exceeded
500internal_error

The optimizer accepts JSON bodies up to 16 KiB and is limited to 6 public requests per 60 seconds per IP by default. On HTTP 429, wait for the number of seconds in the Retry-After response header before retrying.

06

A reliable agent loop

Keep orchestration small and let the response describe its own math.

  1. 1

    Normalize and deduplicate candidate symbols before sending 2–20 tickers.

  2. 2

    Choose the risk metric from the user's stated goal; do not infer that downside and volatility outputs are interchangeable.

  3. 3

    Require a 2xx response, then verify data.missing_tickers and data.ratio_selection_status.

  4. 4

    Pair each chosen value with its *_basis and *_status fields when explaining results.

  5. 5

    Back off on 429 using Retry-After; do not retry validation errors without changing the request.

Educational use only.

Portfolio Crafter uses historical data. The API output is not investment advice, and past performance does not predict future results.