> 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.

# Pull a public company

POST https://api.spreadspace.app/api/loans/{loan_id}/public-companies
Content-Type: application/json

Copies a public company's extracted filings onto the loan as documents,
one charge per company. Pulling a company already on the loan answers
its documents again and charges nothing.

The copies are the company's financial statements from the filings it
filed as the primary registrant, of the named forms from the named year
on: one upload per filing, one document per statement, read on the loan
as any other document. A fresh pull answers 201; a company already on
the loan answers 200 whatever forms and year are named. A fresh pull
without the credit to cover it answers 402, and 409 when the store holds
no statement of the company for those forms and years.

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

Reference: https://docs.spreadspace.app/api/api-reference/loans/pull

## 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

### Path parameters

- `loan_id` (string, required) — The loan's id.

### 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 PullPublicCompanyRequest.

- `registrant_id` (string, required) — The registrant's id: its SEC CIK, one to ten digits, with or without its leading zeros.
- `forms` (list of string, optional, nullable) — The forms the copied statements may come from, among 10-K, 10-Q, 8-K and S-1 in any case, an amendment read with its base form; all four when absent.
- `from_year` (integer, optional, nullable) — The first year the copied statements may come from, 1993 to 2100, a filing's year being its report period's, else its filing date's; every year the store holds when absent.

## Response

### 200

OK

- `registrant` (PublicRegistrantSummary, required) — The company's registrant, with how many of its filings the store holds, its newest filing date and its logo.
- `forms` (list of string, required) — The forms of the copied filings, 10-K, then 10-Q, then 8-K, then S-1; an amendment counts as its base form.
- `from_year` (integer, required) — The earliest year among the copied documents: a document's fiscal year, else its period end's year, else its filing's filing year.
- `pulled_at` (datetime, required) — When the earliest of the copies was made.
- `document_count` (integer, required) — How many documents the copies make on the loan.
- `filings` (list of LoanPublicCompanyFiling, required) — The copied filings, by filing date, then accession.

### 201

Created

- `registrant` (PublicRegistrantSummary, required) — The company's registrant, with how many of its filings the store holds, its newest filing date and its logo.
- `forms` (list of string, required) — The forms of the copied filings, 10-K, then 10-Q, then 8-K, then S-1; an amendment counts as its base form.
- `from_year` (integer, required) — The earliest year among the copied documents: a document's fiscal year, else its period end's year, else its filing's filing year.
- `pulled_at` (datetime, required) — When the earliest of the copies was made.
- `document_count` (integer, required) — How many documents the copies make on the loan.
- `filings` (list of LoanPublicCompanyFiling, required) — The copied filings, by filing date, then accession.

## 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.

### 402 Payment Required Error

Payment required: the standard error envelope whose `type` says why: `subscription_required` (no active or trialing subscription) or `insufficient_credits` (the workspace's prepaid credit cannot cover the pull; the body's `details` carry `balance_usd` and `pull_usd` as decimal strings).

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

### 403 Forbidden Error

Forbidden

- `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.

### 429 Too Many Requests Error

Too many requests: the standard error envelope with `type` `rate_limited` (the endpoint's rate budget), `idempotency_in_progress` (a request with the same `Idempotency-Key` is still executing) or `usage_throttled` (the workspace is under an admission throttle). Honor `Retry-After`.

- `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

### PublicRegistrantSummary

A registrant in the public filings store, with what the store holds for it.

- `cik` (string, required) — The registrant's SEC Central Index Key, ten digits with its leading zeros.
- `name` (string, required) — The registrant's name as filed.
- `tickers` (list of string, required) — The registrant's ticker symbols; empty when it has none.
- `exchanges` (list of string, required) — The exchanges the tickers trade on, in the tickers' order; empty when none.
- `filing_count` (integer, required) — How many of the registrant's filings the store holds, in any state.
- `sic` (string, optional, nullable) — The registrant's four-digit Standard Industrial Classification code, when known.
- `sic_description` (string, optional, nullable) — The classification's description, when known.
- `state` (string, optional, nullable) — The registrant's state of location, when known.
- `fiscal_year_end` (string, optional, nullable) — The fiscal year end as MMDD, when known.
- `latest_filing_date` (date, optional, nullable) — The filing date of the newest filing the store holds; null when none carries a date.
- `logo_url` (string, optional, nullable) — The address of the registrant's logo, a square WebP image: a stored copy when one is kept, else the logo provider's image for the registrant's first ticker; null when the registrant has neither.

### LoanPublicCompanyFiling

One public filing copied onto a loan.

- `accession` (string, required) — The filing's SEC accession number.
- `form` (string, required) — The form type as filed, such as 10-K, 10-Q/A or 8-K.
- `job_id` (string, required) — The id of the upload the filing's copies sit under on the loan.
- `document_ids` (list of string, required) — The ids of the documents copied from the filing, in the filing's order.
- `filing_date` (date, optional, nullable) — The date the filing was made, when known.
- `report_period` (date, optional, nullable) — The period end the filing reports on, when known.

### 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
{
  "registrant_id": "9900077",
  "forms": [
    "10-K",
    "10-Q"
  ],
  "from_year": 2024
}
```

**Response**

```json
{
  "registrant": {
    "cik": "0009900077",
    "name": "Larkspur Mills Inc",
    "tickers": [
      "LKSP"
    ],
    "exchanges": [
      "NYSE"
    ],
    "filing_count": 42,
    "sic": "2211",
    "sic_description": "Broadwoven Fabric Mills, Cotton",
    "state": "NC",
    "fiscal_year_end": "1231",
    "latest_filing_date": "2026-03-02",
    "logo_url": "https://assets.spreadspace.app/0009900077/9f2c1e7a4b8d3f60.webp"
  },
  "forms": [
    "10-K",
    "10-Q"
  ],
  "from_year": 2024,
  "pulled_at": "2026-03-05T16:40:08.512377+00:00",
  "document_count": 5,
  "filings": [
    {
      "accession": "0009900077-25-000041",
      "form": "10-Q",
      "job_id": "2f6e9a1c4b7d3e5f8a0c2b4e",
      "document_ids": [
        "6d0f8b2e4a5c7d9f1b3e5a7c",
        "7e1a9c3f5b6d8e0a2c4f6b8d"
      ],
      "filing_date": "2025-11-05",
      "report_period": "2025-09-30"
    },
    {
      "accession": "0009900077-26-000012",
      "form": "10-K",
      "job_id": "2f6e9a1c4b7d3e5f8a0c2b4d",
      "document_ids": [
        "3a7c5e9b1d2f4a6c8e0b3d5f",
        "4b8d6f0c2e3a5b7d9f1c4e6a",
        "5c9e7a1d3f4b6c8e0a2d4f6b"
      ],
      "filing_date": "2026-03-02",
      "report_period": "2025-12-31"
    }
  ]
}
```

**SDK Code**

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

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

const pulled = await client.loans.publicCompanies.pull('9f8b7c6d5e4a3b2c1d0e4f5a', {
  registrant_id: '9900077',
  forms: ['10-K', '10-Q'],
  from_year: 2024,
});
console.log(pulled.document_count);

```

```python
import os

from spreadspace import SpreadSpace

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

pulled = client.loans.public_companies.pull(
    "9f8b7c6d5e4a3b2c1d0e4f5a",
    {"registrant_id": "9900077", "forms": ["10-K", "10-Q"], "from_year": 2024},
)
print(pulled["document_count"])

```

```csharp
using SpreadSpace;

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

var pulled = await client.Loans.PublicCompanies.PullAsync(
    "9f8b7c6d5e4a3b2c1d0e4f5a",
    new PullPublicCompanyRequest { RegistrantId = "9900077", Forms = ["10-K", "10-Q"], FromYear = 2024 });
Console.WriteLine(pulled.DocumentCount);

```

```go
package main

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

func main() {

	url := "https://api.spreadspace.app/api/loans/9f8b7c6d5e4a3b2c1d0e4f5a/public-companies"

	payload := strings.NewReader("{\n  \"registrant_id\": \"9900077\",\n  \"forms\": [\n    \"10-K\",\n    \"10-Q\"\n  ],\n  \"from_year\": 2024\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/loans/9f8b7c6d5e4a3b2c1d0e4f5a/public-companies")

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  \"registrant_id\": \"9900077\",\n  \"forms\": [\n    \"10-K\",\n    \"10-Q\"\n  ],\n  \"from_year\": 2024\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/loans/9f8b7c6d5e4a3b2c1d0e4f5a/public-companies")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"registrant_id\": \"9900077\",\n  \"forms\": [\n    \"10-K\",\n    \"10-Q\"\n  ],\n  \"from_year\": 2024\n}")
  .asString();
```

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

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.spreadspace.app/api/loans/9f8b7c6d5e4a3b2c1d0e4f5a/public-companies', [
  'body' => '{
  "registrant_id": "9900077",
  "forms": [
    "10-K",
    "10-Q"
  ],
  "from_year": 2024
}',
  '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 = [
  "registrant_id": "9900077",
  "forms": ["10-K", "10-Q"],
  "from_year": 2024
] as [String : Any]

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

let request = NSMutableURLRequest(url: NSURL(string: "https://api.spreadspace.app/api/loans/9f8b7c6d5e4a3b2c1d0e4f5a/public-companies")! 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()
```