SDKs
Expand
Type-safe ?expand[]= usage. The TypeScript SDK narrows response types at compile time; Python and Go narrow at runtime.
Last updated
TL;DR. ?expand[]=field inflates a foreign-key field in the response from an ID
string to the full nested resource. Saves a round trip. Matter's TypeScript SDK
narrows the response type at compile time when expand is provided literally —
Python and Go narrow at runtime via discriminated unions.
Many Matter resources reference others by ID. A Filing references its Entity via
filing.entity ("ent_…"). To get the full Entity in the same response, expand it:
GET /v1/filings/flg_4Kj2m8pQ?expand[]=entityThe response now nests the full Entity:
{
"id": "flg_4Kj2m8pQ",
"object": "filing",
"entity": { // expanded — full Entity object
"id": "ent_Nq3KcAbc",
"object": "entity",
"status": "active",
/* ... */
},
/* ... */
}Only fields marked x-matter-expandable: true in the OpenAPI spec can be expanded. Max
depth is 4 — you can expand filing.entity.registered_agent.address, but not deeper.
Node — compile-time narrowing
The TypeScript SDK uses literal-array generics on expand to narrow the response type
at compile time. Pass the array as a const and the response type swaps the ID
string for the full nested resource.
const filing = await matter.filings.retrieve("flg_4Kj2m8pQ", {
expand: ["entity"] as const,
});
filing.entity.legal_name; // ✓ — type is Entity, autocomplete works
// filing.entity.length; // ✗ — would be on the string ID type without expandMultiple expands compose:
const filing = await matter.filings.retrieve("flg_4Kj2m8pQ", {
expand: ["entity", "documents", "entity.registered_agent"] as const,
});
filing.entity.registered_agent.name; // ✓ depth-2 expand
filing.documents[0].kind; // ✓ array of expanded DocumentsWithout as const, the type widens to string[] and the response type doesn't narrow:
// Anti-pattern — type narrowing is lost.
const expand = ["entity"];
const filing = await matter.filings.retrieve("flg_4Kj2m8pQ", { expand });
filing.entity; // type is string | Entity — you have to disambiguateThe SDK exports a helper to build the expand array with autocomplete:
import { expand } from "@mattermode/node";
await matter.filings.retrieve("flg_4Kj2m8pQ", {
expand: expand("filing").pick("entity", "documents"),
});Python — runtime narrowing
Python's type system can't narrow on a literal-list generic. The SDK narrows at
runtime via Pydantic discriminated unions — the field is typed as EntityID | Entity,
and you check.
filing = matter.filings.retrieve("flg_4Kj2m8pQ", expand=["entity"])
# At runtime, filing.entity is an Entity model. Statically it's EntityID | Entity.
if isinstance(filing.entity, Entity):
print(filing.entity.legal_name)
else:
# filing.entity is the EntityID string — fetch separately
entity = matter.entities.retrieve(filing.entity)To skip the isinstance dance, use the expanded helper which raises if the field
isn't expanded:
from matter import expanded
filing = matter.filings.retrieve("flg_4Kj2m8pQ", expand=["entity"])
entity = expanded(filing.entity) # raises if not expanded; returns Entity
print(entity.legal_name)Multiple expands and depth:
filing = matter.filings.retrieve(
"flg_4Kj2m8pQ",
expand=["entity", "documents", "entity.registered_agent"],
)The SDK validates expand paths against the spec — passing a non-expandable field raises
MatterValidationError before sending the request.
Go — runtime narrowing
Go's type system shares Python's limitation. The expandable field is a struct with two
populated branches — ID always set, Resource set when expanded.
filing, err := client.Filings.Retrieve(ctx, "flg_4Kj2m8pQ", &matter.FilingRetrieveParams{
Expand: []string{"entity"},
})
if err != nil {
return err
}
if filing.Entity.Resource != nil {
fmt.Println(filing.Entity.Resource.LegalName)
} else {
// not expanded — fetch separately
entity, _ := client.Entities.Retrieve(ctx, filing.Entity.ID, nil)
fmt.Println(entity.LegalName)
}To skip the nil check, use the MustExpand helper which panics if not expanded — only
use this when you control the request and know expand was set:
entity := filing.Entity.MustExpand()
fmt.Println(entity.LegalName)Path validation against the spec happens at request time — Expand: []string{"foo"}
on a non-expandable field returns *matter.ValidationError before the request goes
out.
When to use expand
| Situation | Use expand? |
|---|---|
| Building a one-off detail view of one resource | Yes |
| Hot list of 100 resources, want one nested field on each | Yes — saves N round trips |
| You need every nested resource's full graph | No — request the resources separately |
| Webhook handler where the event already carries the resource | No |
The cost on the server side is the same — Matter resolves the same data either way. The savings are network round trips, not server work.
Depth and limits
- Max depth: 4.
a.b.c.dok,a.b.c.d.erejected. - Max expand fields per request: 10. The combined paths cannot exceed 10.
- Only fields tagged
x-matter-expandablein the OpenAPI spec are eligible. Tools that read the spec discover these automatically.
# rejected — depth 5
GET /v1/filings/flg_4Kj2m8pQ?expand[]=entity.registered_agent.address.country.continent// 400 invalid_request
{
"type": "https://mattermode.com/errors/invalid_request",
"code": "expand_depth_exceeded",
"detail": "expand path exceeds max depth of 4"
}