Linear
This connector captures issues, projects, initiatives, and labels from Linear into Estuary collections. It authenticates with a Linear personal API key and reads data through the Linear GraphQL API.
Supported data resources
| Stream | Description | Replication |
|---|---|---|
issues | Issues across every team the authenticating user can access. | Incremental + backfill |
projects | Projects, including their status, dates, and progress. | Incremental + backfill |
initiatives | Initiatives and their owning relationships. Requires a paid Linear plan. | Incremental + backfill |
labels | Issue labels, both workspace-level and team-level. | Snapshot |
issues, projects, and initiatives are keyed on /id and cursored on updatedAt. After
the initial backfill, each sync captures only records changed since the previous sync.
labels is re-read in full every interval, because applying a label to an issue does not
advance the label's updatedAt. Every label is captured regardless of the start date, and
each row carries a /_meta/row_id key instead of /id.
Related records are referenced by id. Each issue carries labelIds, so issues join to the
labels collection, along with { id } references to its team, state, project, cycle, parent
and assignee. Two relationships are not captured: the teams a project belongs to, and the
projects under an initiative.
Only primary keys, cursors, and the archivedAt tombstone are declared on the write schema;
all remaining fields are populated by schema inference, so new Linear fields appear
automatically without a connector change.
Archival and deletion
Archiving a record in Linear does not advance its updatedAt timestamp. This has a
different consequence per stream, and it affects how you should interpret captured data.
-
issuescapture archival. Linear's issue filter exposes anarchivedAtcomparator, so the connector runs a second pass over that field on every sync. An archived issue arrives witharchivedAtset — treat that as the tombstone. Do not infer archival fromupdatedAtmovement, which does not change when an issue is archived.A deleted issue is not removed from your destination. Linear moves it to the trash first, and it arrives with
trashed: trueonly if trashing advancesupdatedAt, which has not been verified. Once Linear purges it, it never arrives again. -
labelscapture archival and deletion. Each snapshot includes archived labels witharchivedAtset, and a label deleted in Linear is removed from your destination. -
projectsandinitiativesdo not capture archival. Linear's API exposes noarchivedAtfilter for these types, so there is no way to detect archival incrementally. A record archived or deleted in Linear remains in your destination indefinitely with a nullarchivedAt, indistinguishable from a live record. Re-running a backfill of the binding picks uparchivedAtfor records updated since the start date. It never removes records, though, so deleted projects and initiatives stay in your destination, and records archived before the start date keep a nullarchivedAt.
Limitations
identifieris not captured for projects or initiatives. Linear gates it behind its paid "Project IDs" and "Initiative IDs" add-ons, and requesting it errors on every page for workspaces without them.initiativesrequires a paid Linear plan. How Linear responds to the stream on a workspace without the entitlement has not been verified. If it returns an error, the binding fails, so disable it on such workspaces.
Rate limits
Linear meters two independent hourly budgets per user:
| Budget | Limit |
|---|---|
| Requests | 2,500 per hour |
| Complexity | 3,000,000 points per hour |
| Single query | 10,000 points (hard cap) |
Either can bind first, because complexity is charged per record returned rather than per
request: workspaces whose records carry many populated relations spend complexity faster than
requests. The connector reads both budgets from every response and pauses until the relevant
window resets, so a healthy capture should not be rate-limited. If Linear still rejects a
request as rate-limited, the connector waits for the budget to reset and retries. If a single
page exceeds the 10,000-point cap, it retries with a smaller page. Reducing binding intervals
across many bindings increases consumption of both budgets proportionally.
Prerequisites
- A Linear account with access to the teams whose data you want to capture. The connector sees exactly what the authenticating user sees.
- A Linear personal API key. See Authentication below.
- A paid Linear plan, if you intend to capture the
initiativesstream.
Authentication
The connector authenticates with a Linear personal API key.
To create one:
- Sign in to Linear as the user whose access the connector should use.
- Go to Settings > Security & access > Personal API keys.
- Click New API key, give it a name, and grant it read access.
- Copy the generated key immediately — Linear only displays it once.
You'll use this value as the access_token when configuring the connector.
Configuration
You configure connectors either in the Estuary web app, or by directly editing the Data Flow specification file. See connectors to learn more about using connectors. The values and specification sample below provide configuration details specific to the Linear source connector.
Properties
Endpoint
| Property | Title | Description | Type | Required/Default |
|---|---|---|---|---|
/credentials | Authentication | Linear API key credentials. | object | Required |
/credentials/access_token | API Key | Linear personal API key, created under Settings > Security & access > Personal API keys. | string | Required |
/start_date | Start Date | UTC date and time from which to start replicating data. Data generated before this date is not replicated, except labels, which are always captured in full. Defaults to 30 days before the present. | string | 30 days ago |
Bindings
| Property | Title | Description | Type | Required/Default |
|---|---|---|---|---|
/name | Name | Name of the resource to capture (issues, projects, initiatives, or labels). | string | Required |
/interval | Interval | Interval between data syncs for this resource. | string | PT5M |
Sample
captures:
${PREFIX}/${CAPTURE_NAME}:
endpoint:
connector:
image: ghcr.io/estuary/source-linear:v1
config:
credentials:
credentials_title: API Key
access_token: <secret>
start_date: "2024-01-01T00:00:00Z"
bindings:
- resource:
name: issues
interval: PT5M
target: ${PREFIX}/issues
- resource:
name: projects
interval: PT5M
target: ${PREFIX}/projects
- resource:
name: initiatives
interval: PT5M
target: ${PREFIX}/initiatives
- resource:
name: labels
interval: PT5M
target: ${PREFIX}/labels