> This page is for API.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.spreadspace.app/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.spreadspace.app/_mcp/server.

# Create sheet

POST https://api.spreadspace.app/api/markets/sheets
Content-Type: application/json

Creates a saved sheet at version 1: an annual or quarterly sheet names a company by its CIK, a comparison, custom or spread sheet names a comp; the document is a JSON object of at most 2,000,000 bytes. An optional client-minted `id` makes a create asked again answer 200 with the sheet that already landed. Needs a Market research subscription and `markets:write`.

Requires the `markets:write` scope. Rate limit: 200 requests per minute per key (the `api` lane).

Reference: https://docs.spreadspace.app/api/api-reference/markets-sheets/create-sheet

## Authentication

- `Authorization` header (bearer token, required) — Bearer token. Use `ss_live_…` for live data or `ss_test_…` for the sandbox (test mode). See [Authentication](https://docs.spreadspace.app/api/authentication).

## Request

### Headers

- `Idempotency-Key` (string, optional) — Idempotency token. Retries that reuse the key within 24h replay the original response. Up to 255 characters; a UUID is typical. See [Idempotency](https://docs.spreadspace.app/api/idempotency).
- `SpreadSpace-Version` (string, optional) — Pin the API version, e.g. `2026-10-07`. Omit to get the latest. See [Versioning](https://docs.spreadspace.app/api/versioning).

### Body (application/json)

This endpoint expects a CreateMarketsSheetRequest.

- `kind` (string, required) — What the sheet is: annual, quarterly, comparison, custom or spread. A spread sheet made here follows no canvas; the canvas's own sheet route makes the one that does.
- `name` (string, required) — The sheet's name, 1 to 200 characters after trimming.
- `doc` (any, required) — The SpreadSheet document, a JSON object of at most 2,000,000 UTF-8 bytes.
- `is_original` (boolean, required, default: false) — Whether this is the company's original sheet (2026-10-29): needs `cik` and a kind of `annual` or `quarterly`. When the company's original of that kind already stands, the create answers 200 with that sheet, unchanged, so a SpreadSheet's first open never makes a second one.
- `id` (string, optional, nullable) — An optional client-minted id, 24 lowercase hexadecimal characters: a create asked again with it answers the sheet that already landed instead of making a second one.
- `cik` (string, optional, nullable) — The company's CIK, one to ten digits, with or without its leading zeros, of a registrant in the store: required on an annual or quarterly sheet, refused on the others.
- `comp_id` (string, optional, nullable) — The id of a comp the caller can see: required on a comparison, custom or spread sheet, refused on an annual or quarterly one.

## Response

### 200

OK

- `sheet` (MarketsSheetSummary, required) — The sheet.
- `doc` (any, required) — The SpreadSheet document as saved, a JSON object.

### 201

Created

- `sheet` (MarketsSheetSummary, required) — The sheet.
- `doc` (any, required) — The SpreadSheet document as saved, a JSON object.

## Errors

### 400 Bad Request Error

Bad Request

- `error` (ApiErrorBody, required) — Inner body of the canonical API error envelope.

### 401 Unauthorized Error

Unauthorized: the bearer token or API key is missing or invalid.

- `error` (ApiErrorBody, required) — Inner body of the canonical API error envelope.

### 403 Forbidden Error

Forbidden: authentication succeeded but the caller is not permitted to perform this action, for example an API key missing the required scope, an embed session reaching a loan it was not minted for, or a feature outside the workspace's plan.

- `error` (ApiErrorBody, required) — Inner body of the canonical API error envelope.

### 404 Not Found Error

Not Found

- `error` (ApiErrorBody, required) — Inner body of the canonical API error envelope.

### 409 Conflict Error

Conflict

- `error` (ApiErrorBody, required) — Inner body of the canonical API error envelope.

### 413 Content Too Large Error

Payload Too Large

- `error` (ApiErrorBody, required) — Inner body of the canonical API error envelope.

### 429 Too Many Requests Error

Too many requests: the rate limit was hit, or an idempotency-key replay is still in progress.

- `error` (ApiErrorBody, required) — Inner body of the canonical API error envelope.

### 500 Internal Server Error

Internal server error: an unexpected failure. Quote the `request_id` in support tickets. A mutation sent with an `Idempotency-Key` replays this fault under that key rather than re-running; confirm the resource state before resending with a new key.

- `error` (ApiErrorBody, required) — Inner body of the canonical API error envelope.

## Types

### MarketsSheetSummary

A saved Markets sheet: a company's SpreadSheet or a comp's, of one kind, without its document.

- `id` (string, required) — The sheet's id.
- `kind` (string, required) — What the sheet is: annual, quarterly, comparison, custom or spread.
- `name` (string, required) — The sheet's name.
- `is_original` (boolean, required) — Whether the sheet is the company's original (2026-10-29): laid out by SpreadSpace from the company's filings and laid out again as new filings land; never edited by hand, never deleted or renamed (403). One per company and sheet kind.
- `version` (integer, required) — The document's version, which the next save asserts; a rename leaves it as it was.
- `created_at` (datetime, required) — When the sheet was created.
- `updated_at` (datetime, required) — When the sheet was last saved or renamed.
- `cik` (string, optional, nullable) — The company's SEC Central Index Key, ten digits with its leading zeros, on a company's sheet (annual or quarterly); null on a comp's sheet.
- `comp_id` (string, optional, nullable) — The id of the comp a comp's sheet (comparison, custom or spread) is over; null on a company's sheet, which sits in no comp.
- `spread_board_id` (string, optional, nullable) — The id of the canvas a sheet of kind spread follows; null on every other sheet, and on a spread sheet whose canvas was deleted.

### ApiErrorBody

Inner body of the canonical API error envelope.

- `type` (enum, required) — Stable error type identifier, one of the values listed. Pattern-match on this value, not the human-readable message. A later version may add a value; treat one you do not know by its HTTP status.
  - Allowed values: `billing_required`, `business_email_required`, `conflict`, `counterparty_not_in_plan`, `cursor_expired`, `custom_visual_limit_exceeded`, `doc_too_large`, `embed_not_in_plan`, `embed_origin_not_allowed`, `embed_scope_exceeds_key`, `embed_scope_not_allowed`, `embed_session_binding_mismatch`, `embed_session_not_found`, `embed_token_expired`, `forbidden`, `geometry_plan_denied`, `html_upload_disabled`, `idempotency_in_progress`, `idempotency_request_mismatch`, `idempotency_response_too_large`, `insufficient_credits`, `insufficient_scope`, `internal_error`, `invalid_code`, `invalid_cursor`, `invalid_embed_token`, `invalid_include`, `invalid_limit`, `invalid_request`, `invalid_session_handle`, `invalid_version`, `key_expired`, `key_revoked`, `last_item`, `license_assigned`, `license_holder_has_license`, `license_none_free`, `license_reassign_window`, `licenses_assigned`, `markets_api_required`, `memo_locked`, `memo_not_locked`, `mfa_enrollment_required`, `mode_mismatch`, `not_found`, `payload_too_large`, `payment_failed`, `pii_claim_required`, `rate_limited`, `render_failed`, `request_cancelled`, `research_not_in_plan`, `resource_gone`, `same_origin_required`, `sandbox_missing`, `sandbox_organization`, `scope_not_in_product`, `scope_state_changed`, `session_handle_already_used`, `session_handle_expired`, `session_handle_revoked`, `spend_cap_reached`, `spread_already_finalized`, `spread_limit`, `spread_locked`, `spread_not_finalized`, `spread_not_locked`, `spread_not_ready`, `spread_template_limit_exceeded`, `spreads_not_in_plan`, `spreadsheet_upload_disabled`, `statement_mapping_not_in_plan`, `subscription_required`, `surface_limit_exceeded`, `test_key_remint_required`, `unauthorized`, `unknown_factor`, `unprocessable`, `usage_throttled`, `validation_failed`
- `message` (string, required) — Human-readable error message. Subject to wording changes; do not parse.
- `code` (string, optional) — A finer, stable identifier within `type`, on the errors that carry one; absent otherwise. Pattern-match on it like `type`. The upload presign answers `not_found` with `loan_not_found` and `invalid_request` with `borrower_loan_mismatch`.
- `request_id` (string, optional, nullable) — Per-request correlation ID (matches `X-Request-ID` response header). Quote in support tickets.
- `details` (map from string to string, optional, nullable) — Structured detail. Some error types populate documented keys in every environment: a version conflict (409) carries `current_version`, the version the resource is actually at, so the client can reload and re-apply. Free-form diagnostic detail beyond those keys appears in development environments only. An `insufficient_credits` 402 carries `balance_usd`, `reserved_usd`, `in_flight_docs`, `incoming_docs` and `per_doc_reservation_usd` as decimal strings.

## Examples

**Request**

```json
{
  "kind": "custom",
  "name": "Margin bridge",
  "doc": {
    "v": 1,
    "filers": {},
    "sheets": [
      {
        "id": "s1a2b3c4",
        "name": "Margin bridge",
        "rows": [
          {
            "id": "r0000001"
          },
          {
            "id": "r0000002"
          }
        ],
        "cols": [
          {
            "id": "c0000001",
            "w": 14
          },
          {
            "id": "c0000002"
          }
        ],
        "cells": {
          "r0000001:c0000001": {
            "v": "Net sales"
          },
          "r0000001:c0000002": {
            "v": 418250000
          },
          "r0000002:c0000001": {
            "v": "Gross profit"
          },
          "r0000002:c0000002": {
            "v": 129657500
          }
        },
        "frozen": {
          "rows": 1,
          "cols": 1
        },
        "merges": [],
        "groups": []
      }
    ],
    "names": {},
    "scenarios": {},
    "activeScenario": null,
    "sens": {}
  },
  "is_original": false,
  "comp_id": "4e1f7a2b9c3d5e6f8a0b1c2d"
}
```

**Response**

```json
{
  "sheet": {
    "id": "8d4f1a6b3c5e7f9a1b2c4d6e",
    "kind": "custom",
    "name": "Margin bridge",
    "is_original": false,
    "version": 1,
    "created_at": "2026-03-09T11:04:27.550318+00:00",
    "updated_at": "2026-03-09T11:04:27.550318+00:00",
    "cik": null,
    "comp_id": "4e1f7a2b9c3d5e6f8a0b1c2d",
    "spread_board_id": null
  },
  "doc": {
    "v": 1,
    "filers": {},
    "sheets": [
      {
        "id": "s1a2b3c4",
        "name": "Margin bridge",
        "rows": [
          {
            "id": "r0000001"
          },
          {
            "id": "r0000002"
          }
        ],
        "cols": [
          {
            "id": "c0000001",
            "w": 14
          },
          {
            "id": "c0000002"
          }
        ],
        "cells": {
          "r0000001:c0000001": {
            "v": "Net sales"
          },
          "r0000001:c0000002": {
            "v": 418250000
          },
          "r0000002:c0000001": {
            "v": "Gross profit"
          },
          "r0000002:c0000002": {
            "v": 129657500
          }
        },
        "frozen": {
          "rows": 1,
          "cols": 1
        },
        "merges": [],
        "groups": []
      }
    ],
    "names": {},
    "scenarios": {},
    "activeScenario": null,
    "sens": {}
  }
}
```

**SDK Code**

```typescript
import { SpreadSpace } from '@spreadspace/sdk';

const client = new SpreadSpace({ apiKey: process.env.SPREADSPACE_API_KEY! });

const created = await client.markets.sheets.create({
  kind: 'custom',
  name: 'Margin bridge',
  comp_id: '4e1f7a2b9c3d5e6f8a0b1c2d',
  is_original: false,
  doc: {
    v: 1,
    filers: {},
    sheets: [
      {
        id: 's1a2b3c4',
        name: 'Margin bridge',
        rows: [{ id: 'r0000001' }, { id: 'r0000002' }],
        cols: [{ id: 'c0000001', w: 14 }, { id: 'c0000002' }],
        cells: {
          'r0000001:c0000001': { v: 'Net sales' },
          'r0000001:c0000002': { v: 418250000 },
          'r0000002:c0000001': { v: 'Gross profit' },
          'r0000002:c0000002': { v: 129657500 },
        },
        frozen: { rows: 1, cols: 1 },
        merges: [],
        groups: [],
      },
    ],
    names: {},
    scenarios: {},
    activeScenario: null,
    sens: {},
  },
});
console.log(created.sheet.id, created.sheet.version);

```

```python
import os

from spreadspace import SpreadSpace

client = SpreadSpace(api_key=os.environ["SPREADSPACE_API_KEY"])

created = client.markets.sheets.create({
    "kind": "custom",
    "name": "Margin bridge",
    "comp_id": "4e1f7a2b9c3d5e6f8a0b1c2d",
    "is_original": False,
    "doc": {
        "v": 1,
        "filers": {},
        "sheets": [
            {
                "id": "s1a2b3c4",
                "name": "Margin bridge",
                "rows": [{"id": "r0000001"}, {"id": "r0000002"}],
                "cols": [{"id": "c0000001", "w": 14}, {"id": "c0000002"}],
                "cells": {
                    "r0000001:c0000001": {"v": "Net sales"},
                    "r0000001:c0000002": {"v": 418250000},
                    "r0000002:c0000001": {"v": "Gross profit"},
                    "r0000002:c0000002": {"v": 129657500},
                },
                "frozen": {"rows": 1, "cols": 1},
                "merges": [],
                "groups": [],
            }
        ],
        "names": {},
        "scenarios": {},
        "activeScenario": None,
        "sens": {},
    },
})
print(created["sheet"]["id"], created["sheet"]["version"])

```

```csharp
using SpreadSpace;

using var client = new SpreadSpaceClient(Environment.GetEnvironmentVariable("SPREADSPACE_API_KEY"));

var doc = System.Text.Json.JsonDocument.Parse("""
    {
      "v": 1,
      "filers": {},
      "sheets": [
        {
          "id": "s1a2b3c4",
          "name": "Margin bridge",
          "rows": [{"id": "r0000001"}, {"id": "r0000002"}],
          "cols": [{"id": "c0000001", "w": 14}, {"id": "c0000002"}],
          "cells": {
            "r0000001:c0000001": {"v": "Net sales"},
            "r0000001:c0000002": {"v": 418250000},
            "r0000002:c0000001": {"v": "Gross profit"},
            "r0000002:c0000002": {"v": 129657500}
          },
          "frozen": {"rows": 1, "cols": 1},
          "merges": [],
          "groups": []
        }
      ],
      "names": {},
      "scenarios": {},
      "activeScenario": null,
      "sens": {}
    }
    """).RootElement;
var created = await client.Markets.Sheets.CreateAsync(new CreateMarketsSheetRequest
{
    Kind = "custom",
    Name = "Margin bridge",
    CompId = "4e1f7a2b9c3d5e6f8a0b1c2d",
    IsOriginal = false,
    Doc = doc,
});
Console.WriteLine($"{created.Sheet.Id} {created.Sheet.Version}");

```

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.spreadspace.app/api/markets/sheets"

	payload := strings.NewReader("{\n  \"kind\": \"custom\",\n  \"name\": \"Margin bridge\",\n  \"doc\": {\n    \"v\": 1,\n    \"filers\": {},\n    \"sheets\": [\n      {\n        \"id\": \"s1a2b3c4\",\n        \"name\": \"Margin bridge\",\n        \"rows\": [\n          {\n            \"id\": \"r0000001\"\n          },\n          {\n            \"id\": \"r0000002\"\n          }\n        ],\n        \"cols\": [\n          {\n            \"id\": \"c0000001\",\n            \"w\": 14\n          },\n          {\n            \"id\": \"c0000002\"\n          }\n        ],\n        \"cells\": {\n          \"r0000001:c0000001\": {\n            \"v\": \"Net sales\"\n          },\n          \"r0000001:c0000002\": {\n            \"v\": 418250000\n          },\n          \"r0000002:c0000001\": {\n            \"v\": \"Gross profit\"\n          },\n          \"r0000002:c0000002\": {\n            \"v\": 129657500\n          }\n        },\n        \"frozen\": {\n          \"rows\": 1,\n          \"cols\": 1\n        },\n        \"merges\": [],\n        \"groups\": []\n      }\n    ],\n    \"names\": {},\n    \"scenarios\": {},\n    \"activeScenario\": null,\n    \"sens\": {}\n  },\n  \"is_original\": false,\n  \"comp_id\": \"4e1f7a2b9c3d5e6f8a0b1c2d\"\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.spreadspace.app/api/markets/sheets")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"kind\": \"custom\",\n  \"name\": \"Margin bridge\",\n  \"doc\": {\n    \"v\": 1,\n    \"filers\": {},\n    \"sheets\": [\n      {\n        \"id\": \"s1a2b3c4\",\n        \"name\": \"Margin bridge\",\n        \"rows\": [\n          {\n            \"id\": \"r0000001\"\n          },\n          {\n            \"id\": \"r0000002\"\n          }\n        ],\n        \"cols\": [\n          {\n            \"id\": \"c0000001\",\n            \"w\": 14\n          },\n          {\n            \"id\": \"c0000002\"\n          }\n        ],\n        \"cells\": {\n          \"r0000001:c0000001\": {\n            \"v\": \"Net sales\"\n          },\n          \"r0000001:c0000002\": {\n            \"v\": 418250000\n          },\n          \"r0000002:c0000001\": {\n            \"v\": \"Gross profit\"\n          },\n          \"r0000002:c0000002\": {\n            \"v\": 129657500\n          }\n        },\n        \"frozen\": {\n          \"rows\": 1,\n          \"cols\": 1\n        },\n        \"merges\": [],\n        \"groups\": []\n      }\n    ],\n    \"names\": {},\n    \"scenarios\": {},\n    \"activeScenario\": null,\n    \"sens\": {}\n  },\n  \"is_original\": false,\n  \"comp_id\": \"4e1f7a2b9c3d5e6f8a0b1c2d\"\n}"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.spreadspace.app/api/markets/sheets")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"kind\": \"custom\",\n  \"name\": \"Margin bridge\",\n  \"doc\": {\n    \"v\": 1,\n    \"filers\": {},\n    \"sheets\": [\n      {\n        \"id\": \"s1a2b3c4\",\n        \"name\": \"Margin bridge\",\n        \"rows\": [\n          {\n            \"id\": \"r0000001\"\n          },\n          {\n            \"id\": \"r0000002\"\n          }\n        ],\n        \"cols\": [\n          {\n            \"id\": \"c0000001\",\n            \"w\": 14\n          },\n          {\n            \"id\": \"c0000002\"\n          }\n        ],\n        \"cells\": {\n          \"r0000001:c0000001\": {\n            \"v\": \"Net sales\"\n          },\n          \"r0000001:c0000002\": {\n            \"v\": 418250000\n          },\n          \"r0000002:c0000001\": {\n            \"v\": \"Gross profit\"\n          },\n          \"r0000002:c0000002\": {\n            \"v\": 129657500\n          }\n        },\n        \"frozen\": {\n          \"rows\": 1,\n          \"cols\": 1\n        },\n        \"merges\": [],\n        \"groups\": []\n      }\n    ],\n    \"names\": {},\n    \"scenarios\": {},\n    \"activeScenario\": null,\n    \"sens\": {}\n  },\n  \"is_original\": false,\n  \"comp_id\": \"4e1f7a2b9c3d5e6f8a0b1c2d\"\n}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.spreadspace.app/api/markets/sheets', [
  'body' => '{
  "kind": "custom",
  "name": "Margin bridge",
  "doc": {
    "v": 1,
    "filers": {},
    "sheets": [
      {
        "id": "s1a2b3c4",
        "name": "Margin bridge",
        "rows": [
          {
            "id": "r0000001"
          },
          {
            "id": "r0000002"
          }
        ],
        "cols": [
          {
            "id": "c0000001",
            "w": 14
          },
          {
            "id": "c0000002"
          }
        ],
        "cells": {
          "r0000001:c0000001": {
            "v": "Net sales"
          },
          "r0000001:c0000002": {
            "v": 418250000
          },
          "r0000002:c0000001": {
            "v": "Gross profit"
          },
          "r0000002:c0000002": {
            "v": 129657500
          }
        },
        "frozen": {
          "rows": 1,
          "cols": 1
        },
        "merges": [],
        "groups": []
      }
    ],
    "names": {},
    "scenarios": {},
    "activeScenario": null,
    "sens": {}
  },
  "is_original": false,
  "comp_id": "4e1f7a2b9c3d5e6f8a0b1c2d"
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```swift
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = [
  "kind": "custom",
  "name": "Margin bridge",
  "doc": [
    "v": 1,
    "filers": [],
    "sheets": [
      [
        "id": "s1a2b3c4",
        "name": "Margin bridge",
        "rows": [["id": "r0000001"], ["id": "r0000002"]],
        "cols": [[
            "id": "c0000001",
            "w": 14
          ], ["id": "c0000002"]],
        "cells": [
          "r0000001:c0000001": ["v": "Net sales"],
          "r0000001:c0000002": ["v": 418250000],
          "r0000002:c0000001": ["v": "Gross profit"],
          "r0000002:c0000002": ["v": 129657500]
        ],
        "frozen": [
          "rows": 1,
          "cols": 1
        ],
        "merges": [],
        "groups": []
      ]
    ],
    "names": [],
    "scenarios": [],
    "activeScenario": ,
    "sens": []
  ],
  "is_original": false,
  "comp_id": "4e1f7a2b9c3d5e6f8a0b1c2d"
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.spreadspace.app/api/markets/sheets")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```