From estuary-schema
Creates an Alpaca capture using flowctl to stream stock trade data into Estuary collections for historical backfill and real-time streaming.
How this skill is triggered — by the user, by Claude, or both
Slash command
/estuary-schema:capture-alpaca-createThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Create an Alpaca capture using flowctl to stream stock trade data from the Alpaca Market Data API into Estuary collections — both a historical backfill and a low-latency real-time stream, in parallel.
Create an Alpaca capture using flowctl to stream stock trade data from the Alpaca Market Data API into Estuary collections — both a historical backfill and a low-latency real-time stream, in parallel.
Applies to: source-alpaca (Alpaca Market Data connector)
Before proceeding, fetch the official connector docs for prerequisites, config reference, and limitations.
Load the docs page: https://docs.estuary.dev/reference/Connectors/capture-connectors/alpaca/
Use WebFetch to load this page. It covers:
iex vs. Unlimited/sip)Search Kapa for tribal knowledge (if the Estuary MCP is configured):
Search kapa ai knowledge sources for "capture alpaca common issues"
If Kapa MCP is not configured, the user can set it up: https://docs.estuary.dev/features/mcp-integration/
This skill provides the flowctl workflow and troubleshooting that docs don't cover.
Alpaca captures trade data using two APIs at once:
start_date forward until it catches up to the present.All symbols in a capture land in a single collection via one binding (trades). Because the backfill catches up to where the live stream began, the two streams overlap and produce some duplicate trade documents — this is expected and reconcilable (see Limitations and Troubleshooting).
Before writing any YAML, ask the user:
feed and is_free_plan settings:
feed: iex, advanced.is_free_plan: true. Data is a smaller sample and delayed 15 minutes.feed: sip for complete, real-time SIP data.2016-01-01T00:00:00Z (Alpaca's earliest available data). Note this has no effect if changed after the capture has started.advanced.stop_date to halt the historical backfill at a fixed date (e.g. for a bounded one-time load).advanced.disable_real_time: true gives a backfill-only load; advanced.disable_backfill: true gives a stream-only capture.Always use the latest numbered version tag. Query the connector registry to find it:
flowctl raw get --table connector_tags \
--query 'documentation_url=eq.https://go.estuary.dev/source-alpaca' \
--query 'select=image_tag,documentation_url' \
--output yaml
Use the returned image_tag — never hardcode a version. (The docs sample shows :v1, but confirm against the registry.)
feed: sip without an Unlimited subscription will fail with a subscription/permission error.No browser redirect is required — once the keys are in hand, everything else runs in flowctl.
Build flow.yaml using the config reference from the docs:
captures:
<TENANT>/<PATH>/source-alpaca:
endpoint:
connector:
image: ghcr.io/estuary/source-alpaca:<VERSION>
config:
api_key_id: "<ALPACA_API_KEY_ID>"
api_secret_key: "<ALPACA_API_SECRET_KEY>"
feed: iex # iex (free) or sip (Unlimited plan)
start_date: "2022-11-01T00:00:00Z" # must be >= 2016-01-01T00:00:00Z
symbols: "AAPL,MSFT,AMZN,TSLA,GOOGL" # max 20, comma-separated
advanced:
is_free_plan: true # set true on a Free plan (delays data 15 min)
# disable_backfill: false # set true for a stream-only capture
# disable_real_time: false # set true for a backfill-only load
# stop_date: "2023-01-01T00:00:00Z"
# max_backfill_interval: "24h" # smaller helps when tracking many symbols
# min_backfill_interval: "1m"
bindings:
- resource:
name: trades
target: <TENANT>/<PATH>/trades
Important:
feed and is_free_plan must match the account's plan. Free plan → feed: iex and is_free_plan: true.start_date must be >= 2016-01-01T00:00:00Z, and is effectively immutable once the capture starts.symbols is a single comma-separated string, not a YAML list, and must contain ≤ 20 symbols.max_backfill_interval / min_backfill_interval are Go duration strings (e.g. 24h, 1m), not numbers.trades collection.api_secret_key is a secret — don't commit it in plain text. Encrypt it with Estuary's sops-based mechanism:
sops --encrypt \
--input-type yaml --output-type yaml \
--encrypted-suffix "_key" \
--gcp-kms projects/<PROJECT>/locations/global/keyRings/<RING>/cryptoKeys/<KEY> \
flow.yaml > flow.encrypted.yaml
mv flow.encrypted.yaml flow.yaml
Note:
--encrypted-suffix "_key"matches bothapi_key_idandapi_secret_key. The key ID isn't strictly secret, but encrypting both is harmless. flowctl decrypts sops-encrypted specs at publish time. See https://docs.estuary.dev/concepts/connectors/#protecting-secrets for AWS KMS / Azure Key Vault / age options.
# Validate the spec and confirm the trades binding
flowctl discover --source flow.yaml
# Review the spec
cat flow.yaml
# Publish the capture
flowctl catalog publish --source flow.yaml --auto-approve
The connector exposes a single trades resource, so discovery is mostly a validation step here — you can also author the binding by hand as shown in Step 4.
# Check status
flowctl catalog status <TENANT>/<PATH>/source-alpaca
# View logs
flowctl logs --task <TENANT>/<PATH>/source-alpaca --since 5m | jq -c '{ts, message}'
# Read captured trade data
flowctl collections read --collection <TENANT>/<PATH>/trades --uncommitted | head -10
Status progression:
PENDING — Normal for ~30 seconds during shard assignmentBACKFILLING — Historical backfill via the Trades REST API (runs in parallel with the live stream)OK — Backfill caught up and/or real-time websocket stream runningWhat to expect in the data:
iex-only sample.Capturing more than 20 symbols in a single capture can cause API errors. To track more, split symbols across multiple captures (each writing to its own collection).
The historical backfill and the real-time stream overlap, producing duplicate trade documents with identical Alpaca properties but different Estuary metadata. Resolve them downstream:
lastWriteWins reductions.401 Unauthorized / 403 Forbidden / "access key verification failed"Cause: Wrong api_key_id / api_secret_key, a regenerated/revoked key, or keys from the wrong Alpaca environment.
Fix:
Cause: feed: sip was set without an Alpaca Unlimited subscription. The full SIP feed requires a paid plan.
Fix: Either upgrade the Alpaca plan, or switch to the free feed: set feed: iex and advanced.is_free_plan: true. Free-plan data is an iex sample delayed 15 minutes.
Cause: Free plan. is_free_plan: true intentionally delays data by 15 minutes, and iex is a partial sample of the market.
Fix: This is expected on the Free plan. For complete, low-latency data, upgrade to Unlimited and use feed: sip (and remove/disable is_free_plan).
Cause: Alpaca permits only one concurrent market-data websocket connection per account. Running a second Alpaca capture, or using the same keys for another websocket client (a trading bot, a notebook, etc.), contends for that single connection.
Fix:
advanced.disable_real_time: true on all but one capture (the others become backfill-only).Cause: start_date is before 2016-01-01T00:00:00Z (Alpaca's earliest data), or not an RFC 3339 timestamp.
Fix: Set start_date to 2016-01-01T00:00:00Z or later, in full timestamp form (e.g. 2022-11-01T00:00:00Z).
start_date did nothingCause: start_date only takes effect at capture creation — it has no effect if changed after the capture has started.
Fix: To re-backfill from an earlier date, bump the binding's backfill counter (re-reads from the configured start_date) or delete and recreate the capture with the new start_date.
bindings:
- resource: { name: trades }
target: <TENANT>/<PATH>/trades
backfill: 1 # increment to force a fresh backfill
Cause: More than 20 symbols in one capture, or large backfill windows straining the REST API.
Fix:
symbols to ≤ 20; split across multiple captures otherwise.advanced.max_backfill_interval (e.g. "6h" or "1h") so each backfill request covers a smaller window — useful when tracking many symbols.Cause: Expected — the backfill overlaps the real-time stream (see Limitations).
Fix: Materialize with standard updates (Estuary deduplicates automatically), or for delta-updates-only destinations ensure lastWriteWins-equivalent reduction support.
Cause: Common config mistakes:
symbols written as a YAML list instead of a comma-separated stringmax_backfill_interval / min_backfill_interval given as a number instead of a Go duration string ("24h", "1m")is_free_plan not set while using feed: iex, or feed missing entirelyFix: Match the spec in Step 4. symbols is one string; durations are quoted Go-duration strings; feed is required.
Wait 30-60 seconds — this is normal during shard assignment. If still stuck:
flowctl logs --task <TENANT>/<PATH>/source-alpaca --since 5m | jq 'select(.level == "error")'
Cause: U.S. equity markets are closed (no live trades to stream), or advanced.disable_real_time: true is set, or the Free plan's 15-minute delay hasn't elapsed yet.
Fix:
disable_real_time is not set.materialize-clickhouse-create / materialize-snowflake-create / materialize-bigquery-create — Send captured trades to a warehouse (use standard updates to dedupe)estuary-connector-restart — Pause/restart the captureestuary-logs — Deep log analysisestuary-catalog-status — Status checkingestuary-task-stats — Confirm trade throughputnpx claudepluginhub estuary/agent-skills --plugin estuary-schemaGuides collaborative design exploration before implementation: explores context, asks clarifying questions, proposes approaches, and writes a design doc for user approval.
Creates structured, bite-sized implementation plans from specs or requirements before writing code. Useful for breaking down multi-step tasks into testable steps with file structure and task boundaries.
Resolves in-progress git merge or rebase conflicts by analyzing history, understanding intent, and preserving both changes where possible. Runs automated checks after resolution.