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

# Retrieve a W-2

GET https://api.spreadspace.app/api/borrowers/{borrower_id}/extractions/{doc_id}/w2

Retrieves a single extracted Form W-2. Requesting a document of any other type returns a 404.

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

Reference: https://docs.spreadspace.app/api/api-reference/w2s/get-w-2

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

- `borrower_id` (string, required) — The borrower's id.
- `doc_id` (string, required) — The document's id.

### Headers

- `SpreadSpace-Version` (string, optional) — Pin the API version, e.g. `2026-07-19`. Omit to get the latest. See [Versioning](https://docs.spreadspace.app/api/versioning).

## Response

### 200

OK

- `form` ("w2", required) — Always `w2`.
- `data` (W2ReportData_W2Data, required) — The boxes of the first statement in the file, with every statement of the file under `forms_w2`.
- `metadata` (W2ReportData_W2Metadata, required) — How the file was read.
- `k1_partners` (list of string, required) — Always empty on a Form W-2.
- `statements` (list of string, required) — Always empty on a Form W-2.
- `depreciation_reports` (W2ReportDataDepreciationReports, optional, nullable) — Always null on a Form W-2.

## Errors

### 400 Bad Request Error

Bad request: validation failed, the cursor is malformed, or the API version is unknown.

- `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 with `type` `subscription_required` when the workspace has no active or trialing subscription, or `billing_required` where a plan entitlement applies.

- `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: the resource does not exist or is not visible to the calling tenant.

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

### W2ReportData_W2Data

The statements a Form W-2 upload prints, the first statement's boxes at the top level, every statement under forms_w2, and the employee's identity beside them.

- `copy` (string, required, nullable) — The copy letter printed on the form (A, B, C, D, 1 or 2), when the print carries one.
- `tax_year` (integer, required, nullable) — The tax year printed beside the form title.
- `control_number` (string, required, nullable) — Box d, control number, as printed.
- `employer_ein` (string, required, nullable) — Box b, employer identification number, masked to the last four digits.
- `employer_name` (string, required, nullable) — Box c, the employer's name.
- `employer_address` (string, required, nullable) — Box c, the employer's address below the name, on one line.
- `employee_name` (string, required, nullable) — Box e, the employee's first name, initial and last name, as printed.
- `employee_ssn_last4` (string, required, nullable) — Box a, the last four digits of the employee's social security number; the rest of the number is never served.
- `box_1_wages` (string, required, nullable) — Box 1, wages, tips, other compensation.
- `box_2_federal_tax_withheld` (string, required, nullable) — Box 2, federal income tax withheld.
- `box_3_ss_wages` (string, required, nullable) — Box 3, social security wages.
- `box_4_ss_tax_withheld` (string, required, nullable) — Box 4, social security tax withheld.
- `box_5_medicare_wages` (string, required, nullable) — Box 5, Medicare wages and tips.
- `box_6_medicare_tax_withheld` (string, required, nullable) — Box 6, Medicare tax withheld.
- `box_7_ss_tips` (string, required, nullable) — Box 7, social security tips.
- `box_8_allocated_tips` (string, required, nullable) — Box 8, allocated tips.
- `box_10_dependent_care_benefits` (string, required, nullable) — Box 10, dependent care benefits.
- `box_11_nonqualified_plans` (string, required, nullable) — Box 11, nonqualified plans.
- `box_12` (list of W2ReportData_W2Box12Item, required) — Box 12, one entry per printed code and amount, 12a through 12d.
- `box_13_statutory_employee` (boolean, required, nullable) — Box 13, the statutory employee box, true when checked.
- `box_13_retirement_plan` (boolean, required, nullable) — Box 13, the retirement plan box, true when checked.
- `box_13_third_party_sick_pay` (boolean, required, nullable) — Box 13, the third-party sick pay box, true when checked.
- `box_14_other` (list of W2ReportData_W2Box14Item, required) — Box 14, other, one entry per printed label and amount.
- `box_14b_tipped_occupation_codes` (list of string, required) — Box 14b, Treasury tipped occupation codes, on a form that prints the box.
- `state_rows` (list of W2ReportData_W2StateRow, required) — Boxes 15 through 17, one row per state.
- `local_rows` (list of W2ReportData_W2LocalRow, required) — Boxes 18 through 20, one row per locality.
- `omb_number` (string, required, nullable) — The OMB number printed on the form, 1545-0008 or 1545-0029.
- `layout` (enum, required) — The printing of the statement: irs_1up, the IRS form with one statement per page; irs_4up, the four-per-page sheet; substitute, a payroll or preparer print.
  - Allowed values: `irs_1up`, `irs_4up`, `substitute`
- `source_pages` (list of integer, required) — The pages of the uploaded file the statement is printed on, counting from one; several when the upload repeats it.
- `rescued_fields` (list of string, required) — The boxes whose figure was read from the page as a whole rather than from the box itself; empty when every figure was read from its box.
- `forms_w2` (list of W2ReportData_FormW2, required) — Every distinct statement the upload prints, one per employer, in document order; the first is the one at the top level.
- `taxpayer_name` (string, required, nullable) — The employee's name printed in box e of the first statement.
- `ssn_last4` (string, required, nullable) — The last four digits of the employee's social security number printed in box a of the first statement.

### W2ReportData_W2Metadata

How the file was read.

- `form_key` (string, required) — The form's key, w2.
- `detected_year` (integer, required, nullable) — The tax year the form was printed for.
- `parent_form` (string, required, nullable) — Null on a form uploaded on its own.
- `faces_read` (integer, required) — The number of statement faces printed across the upload, every copy counted, on a Form W-2.
- `copies_collapsed` (integer, required) — The number of printed faces that repeat another face of the same statement, on a Form W-2.
- `w2_count` (integer, required) — The number of distinct statements the upload prints, one per employer, on a Form W-2.
- `base_page` (integer, optional) — The page of the uploaded file the form starts on, counting from zero.
- `layout` (enum, optional) — The printing of the first statement on a Form W-2: irs_1up, the IRS form with one statement per page; irs_4up, the four-per-page sheet; substitute, a payroll or preparer print.
  - Allowed values: `irs_1up`, `irs_4up`, `substitute`
- `rescued_fields` (list of string, optional) — The boxes whose figure the page-level read supplied rather than the box itself, on a Form W-2; absent when every figure was read from its box.
- `standalone` (boolean, optional) — True on a form uploaded on its own.

### W2ReportDataDepreciationReports

Always null on a Form W-2.

### ApiErrorBody

Inner body of the canonical API error envelope.

- `type` (string, required) — Stable error type identifier. Pattern-match on this value, not the human-readable message.
- `message` (string, required) — Human-readable error message. Subject to wording changes; do not parse.
- `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.

### W2ReportData_W2Box12Item

One box 12 entry of a Form W-2.

- `code` (string, required, nullable) — The one or two letter code printed beside the amount.
- `amount` (string, required, nullable) — The amount printed for the code.
- `slot` (string, optional, nullable) — The letter of the box 12 line the entry is printed on, a through d, when the form letters its box 12 lines; null when the form prints one box 12 caption.

### W2ReportData_W2Box14Item

One box 14 entry of a Form W-2.

- `label` (string, required, nullable) — The label the employer printed.
- `amount` (string, required, nullable) — The amount printed beside the label.

### W2ReportData_W2StateRow

One state row of a Form W-2, boxes 15 through 17.

- `state` (string, required, nullable) — Box 15, the state.
- `employer_state_id` (string, required, nullable) — Box 15, employer's state ID number.
- `box_16_state_wages` (string, required, nullable) — Box 16, state wages, tips, etc.
- `box_17_state_tax` (string, required, nullable) — Box 17, state income tax.

### W2ReportData_W2LocalRow

One local row of a Form W-2, boxes 18 through 20.

- `box_18_local_wages` (string, required, nullable) — Box 18, local wages, tips, etc.
- `box_19_local_tax` (string, required, nullable) — Box 19, local income tax.
- `box_20_locality` (string, required, nullable) — Box 20, locality name.

### W2ReportData_FormW2

Form W-2, Wage and Tax Statement, one employer's statement of one employee's wages and withholding for one year.

- `copy` (string, required, nullable) — The copy letter printed on the form (A, B, C, D, 1 or 2), when the print carries one.
- `tax_year` (integer, required, nullable) — The tax year printed beside the form title.
- `control_number` (string, required, nullable) — Box d, control number, as printed.
- `employer_ein` (string, required, nullable) — Box b, employer identification number, masked to the last four digits.
- `employer_name` (string, required, nullable) — Box c, the employer's name.
- `employer_address` (string, required, nullable) — Box c, the employer's address below the name, on one line.
- `employee_name` (string, required, nullable) — Box e, the employee's first name, initial and last name, as printed.
- `employee_ssn_last4` (string, required, nullable) — Box a, the last four digits of the employee's social security number; the rest of the number is never served.
- `box_1_wages` (string, required, nullable) — Box 1, wages, tips, other compensation.
- `box_2_federal_tax_withheld` (string, required, nullable) — Box 2, federal income tax withheld.
- `box_3_ss_wages` (string, required, nullable) — Box 3, social security wages.
- `box_4_ss_tax_withheld` (string, required, nullable) — Box 4, social security tax withheld.
- `box_5_medicare_wages` (string, required, nullable) — Box 5, Medicare wages and tips.
- `box_6_medicare_tax_withheld` (string, required, nullable) — Box 6, Medicare tax withheld.
- `box_7_ss_tips` (string, required, nullable) — Box 7, social security tips.
- `box_8_allocated_tips` (string, required, nullable) — Box 8, allocated tips.
- `box_10_dependent_care_benefits` (string, required, nullable) — Box 10, dependent care benefits.
- `box_11_nonqualified_plans` (string, required, nullable) — Box 11, nonqualified plans.
- `box_12` (list of W2ReportData_W2Box12Item, required) — Box 12, one entry per printed code and amount, 12a through 12d.
- `box_13_statutory_employee` (boolean, required, nullable) — Box 13, the statutory employee box, true when checked.
- `box_13_retirement_plan` (boolean, required, nullable) — Box 13, the retirement plan box, true when checked.
- `box_13_third_party_sick_pay` (boolean, required, nullable) — Box 13, the third-party sick pay box, true when checked.
- `box_14_other` (list of W2ReportData_W2Box14Item, required) — Box 14, other, one entry per printed label and amount.
- `box_14b_tipped_occupation_codes` (list of string, required) — Box 14b, Treasury tipped occupation codes, on a form that prints the box.
- `state_rows` (list of W2ReportData_W2StateRow, required) — Boxes 15 through 17, one row per state.
- `local_rows` (list of W2ReportData_W2LocalRow, required) — Boxes 18 through 20, one row per locality.
- `omb_number` (string, required, nullable) — The OMB number printed on the form, 1545-0008 or 1545-0029.
- `layout` (enum, required) — The printing of the statement: irs_1up, the IRS form with one statement per page; irs_4up, the four-per-page sheet; substitute, a payroll or preparer print.
  - Allowed values: `irs_1up`, `irs_4up`, `substitute`
- `source_pages` (list of integer, required) — The pages of the uploaded file the statement is printed on, counting from one; several when the upload repeats it.
- `rescued_fields` (list of string, required) — The boxes whose figure was read from the page as a whole rather than from the box itself; empty when every figure was read from its box.

## Examples

**Response**

```json
{
  "form": "w2",
  "data": {
    "copy": "B",
    "tax_year": 2025,
    "control_number": "A-1001",
    "employer_ein": "XX-XXX0123",
    "employer_name": "Northfield Supply LLC",
    "employer_address": "100 Harbor Way, Riverton, OH 43000",
    "employee_name": "Ada Marsh",
    "employee_ssn_last4": "1234",
    "box_1_wages": "51400.00",
    "box_2_federal_tax_withheld": "6240.00",
    "box_3_ss_wages": "54000.00",
    "box_4_ss_tax_withheld": "3348.00",
    "box_5_medicare_wages": "54000.00",
    "box_6_medicare_tax_withheld": "783.00",
    "box_7_ss_tips": null,
    "box_8_allocated_tips": null,
    "box_10_dependent_care_benefits": "1200.00",
    "box_11_nonqualified_plans": null,
    "box_12": [
      {
        "code": "D",
        "amount": "2600.00",
        "slot": "a"
      },
      {
        "code": "DD",
        "amount": "8400.00",
        "slot": "b"
      }
    ],
    "box_13_statutory_employee": false,
    "box_13_retirement_plan": true,
    "box_13_third_party_sick_pay": false,
    "box_14_other": [
      {
        "label": "SDI",
        "amount": "468.00"
      },
      {
        "label": "UNION DUES",
        "amount": "300.00"
      }
    ],
    "box_14b_tipped_occupation_codes": [],
    "state_rows": [
      {
        "state": "OH",
        "employer_state_id": "OH-0000123",
        "box_16_state_wages": "54000.00",
        "box_17_state_tax": "1620.00"
      },
      {
        "state": "PA",
        "employer_state_id": "PA-0000456",
        "box_16_state_wages": "2000.00",
        "box_17_state_tax": "60.00"
      }
    ],
    "local_rows": [
      {
        "box_18_local_wages": "54000.00",
        "box_19_local_tax": "540.00",
        "box_20_locality": "RIVERTON"
      }
    ],
    "omb_number": "1545-0008",
    "layout": "irs_1up",
    "source_pages": [
      1
    ],
    "rescued_fields": [],
    "forms_w2": [
      {
        "copy": "B",
        "tax_year": 2025,
        "control_number": "A-1001",
        "employer_ein": "XX-XXX0123",
        "employer_name": "Northfield Supply LLC",
        "employer_address": "100 Harbor Way, Riverton, OH 43000",
        "employee_name": "Ada Marsh",
        "employee_ssn_last4": "1234",
        "box_1_wages": "51400.00",
        "box_2_federal_tax_withheld": "6240.00",
        "box_3_ss_wages": "54000.00",
        "box_4_ss_tax_withheld": "3348.00",
        "box_5_medicare_wages": "54000.00",
        "box_6_medicare_tax_withheld": "783.00",
        "box_7_ss_tips": null,
        "box_8_allocated_tips": null,
        "box_10_dependent_care_benefits": "1200.00",
        "box_11_nonqualified_plans": null,
        "box_12": [
          {
            "code": "D",
            "amount": "2600.00",
            "slot": "a"
          },
          {
            "code": "DD",
            "amount": "8400.00",
            "slot": "b"
          }
        ],
        "box_13_statutory_employee": false,
        "box_13_retirement_plan": true,
        "box_13_third_party_sick_pay": false,
        "box_14_other": [
          {
            "label": "SDI",
            "amount": "468.00"
          },
          {
            "label": "UNION DUES",
            "amount": "300.00"
          }
        ],
        "box_14b_tipped_occupation_codes": [],
        "state_rows": [
          {
            "state": "OH",
            "employer_state_id": "OH-0000123",
            "box_16_state_wages": "54000.00",
            "box_17_state_tax": "1620.00"
          },
          {
            "state": "PA",
            "employer_state_id": "PA-0000456",
            "box_16_state_wages": "2000.00",
            "box_17_state_tax": "60.00"
          }
        ],
        "local_rows": [
          {
            "box_18_local_wages": "54000.00",
            "box_19_local_tax": "540.00",
            "box_20_locality": "RIVERTON"
          }
        ],
        "omb_number": "1545-0008",
        "layout": "irs_1up",
        "source_pages": [
          1
        ],
        "rescued_fields": []
      }
    ],
    "taxpayer_name": "Ada Marsh",
    "ssn_last4": "1234"
  },
  "metadata": {
    "form_key": "w2",
    "detected_year": 2025,
    "parent_form": null,
    "faces_read": 1,
    "copies_collapsed": 0,
    "w2_count": 1,
    "base_page": 0,
    "layout": "irs_1up",
    "standalone": true
  },
  "k1_partners": [],
  "statements": [],
  "depreciation_reports": null
}
```

**SDK Code**

```python
import requests

url = "https://api.spreadspace.app/api/borrowers/3f9c2b1e8d7a4c5b9e0f1a2b/extractions/7a1d4e9c2b3f4a5d8e6c1b0f/w2"

headers = {"Authorization": "Bearer <token>"}

response = requests.get(url, headers=headers)

print(response.json())
```

```javascript
const url = 'https://api.spreadspace.app/api/borrowers/3f9c2b1e8d7a4c5b9e0f1a2b/extractions/7a1d4e9c2b3f4a5d8e6c1b0f/w2';
const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

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

func main() {

	url := "https://api.spreadspace.app/api/borrowers/3f9c2b1e8d7a4c5b9e0f1a2b/extractions/7a1d4e9c2b3f4a5d8e6c1b0f/w2"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("Authorization", "Bearer <token>")

	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/borrowers/3f9c2b1e8d7a4c5b9e0f1a2b/extractions/7a1d4e9c2b3f4a5d8e6c1b0f/w2")

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

request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'

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.get("https://api.spreadspace.app/api/borrowers/3f9c2b1e8d7a4c5b9e0f1a2b/extractions/7a1d4e9c2b3f4a5d8e6c1b0f/w2")
  .header("Authorization", "Bearer <token>")
  .asString();
```

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

$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://api.spreadspace.app/api/borrowers/3f9c2b1e8d7a4c5b9e0f1a2b/extractions/7a1d4e9c2b3f4a5d8e6c1b0f/w2', [
  'headers' => [
    'Authorization' => 'Bearer <token>',
  ],
]);

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

```csharp
using RestSharp;

var client = new RestClient("https://api.spreadspace.app/api/borrowers/3f9c2b1e8d7a4c5b9e0f1a2b/extractions/7a1d4e9c2b3f4a5d8e6c1b0f/w2");
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", "Bearer <token>");
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = ["Authorization": "Bearer <token>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://api.spreadspace.app/api/borrowers/3f9c2b1e8d7a4c5b9e0f1a2b/extractions/7a1d4e9c2b3f4a5d8e6c1b0f/w2")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers

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