SEC Filings to Markdown for AI (10-K, 10-Q, 8-K)
Pricing
$3.00 / 1,000 filing converteds
SEC Filings to Markdown for AI (10-K, 10-Q, 8-K)
Convert SEC 10-K, 10-Q and 8-K filings into clean, section-split markdown with tables and metadata. Official SEC endpoints only.
Pricing
$3.00 / 1,000 filing converteds
Rating
0.0
(0)
Developer
Plainpage Labs
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
6 days ago
Last modified
Categories
Share
Get clean, structured SEC filing Markdown that AI systems can use immediately, with traceable sources, explicit coverage limits and predictable per-filing pricing. The public Apify Actor is plainpagelabs/sec-edgar-to-markdown (Actor ID xSMbJOguF9LsXfUuu). Choose filings for one nested filing result, chunks for token-bounded rows, or artifacts for a compact dataset row linking to the complete stored filing. One eligible accession is one billing unit per run, regardless of registrant count or chunk rows; a fully delivered useful partial may qualify, while errors, no-match, not-applicable-only results and incomplete delivery do not. See the output field reference below for fields and the run summary. The summary is a reporting snapshot, not an Apify invoice. The parser is provided as-is: labels are heuristic and do not promise lossless or complete extraction or accurate financial values. Check important sections and amounts against the original filing. This Actor is not affiliated with the SEC.
Pricing: $0.003 per filing
You pay $0.003 for each eligible filing successfully delivered ($3 per 1,000). Apify platform usage is included. For example, 1 eligible filing is $0.003, 100 are $0.30 and 1,000 are $3.
One billing unit is one eligible filing accession delivered in a run, regardless of how many registrants it contains or how many chunk rows it creates. A fully delivered, useful partial filing may be eligible; check its coverage warnings. Fetch or conversion errors, no-match, not-applicable-only content, and incomplete delivery are not charged.
Prefilled example
The input form is prefilled with one fixed public filing:
- JPMorgan Chase & Co. 8-K, filed June 24, 2026
- CIK
0000019617; accession0001628280-26-045167 - Main document
jpm-20260624.htm
The form starts with tickers: [], ciks: ["0000019617"], accessionNumbers: ["0001628280-26-045167"], formTypes: ["8-K"], maxFilings: 1, sections: [], outputMode: "filings", includeExhibits: false, includeFullMarkdown: false, and chunkSize: 0. sections: [] asks for all detected sections. This sample is a UI prefill only; it does not add a company or accession default to custom API requests. Change or remove the CIK/accession before using a different filing.
If you leave contact blank, we use our own contact. If you supply yours, you’re responsible for its accuracy; use an email you monitor. All contact identities share the same SEC request limiter. Redis-backed global coordination is optional: without complete coordinator settings, the Actor logs SEC_RATE_LOCAL_ONLY at INFO and uses an in-process serialized 500 ms start gap that does not coordinate separate runs; if configured Redis becomes unavailable, requests fail closed.
Use with AI agents (MCP)
Use this tool when an AI agent needs SEC EDGAR 10-K, 10-Q or 8-K text as Markdown or chunks. Connect through the Apify MCP server. Find plainpagelabs/sec-edgar-to-markdown with search-actors, then inspect its inputs and pricing with fetch-actor-details.
Minimal call-actor arguments for one exact filing (a real run costing $0.003 if eligible output is delivered; platform usage included):
{"actor": "plainpagelabs/sec-edgar-to-markdown","input": {"ciks": ["0000019617"],"accessionNumbers": ["0001628280-26-045167"],"maxFilings": 1,"outputMode": "filings"},"callOptions": { "maxTotalChargeUsd": 0.003 }}
Run limits belong in callOptions, outside Actor input. call-actor returns run status and storage IDs, not Markdown. If still running, wait with get-actor-run; do not start another paid run to poll. Read results with get-dataset-items (including further pages) and the RUN_SUMMARY record with get-key-value-store-record; check its actorRunId.
Start with filings mode. For flat chunk rows, set outputMode: "chunks" and chunkSize: 2000, leaving full Markdown and exhibits off. Artifacts contain the selected result, not automatically the full document. Check recordType, coverage warnings and billing status: useful partial results may be billed; billed: null means unknown, not zero. Never blindly restart an uncertain run or charge. See Apify's MCP tools and result retrieval.
Start a run from Python
This standard-library example starts one real filing run, polls its status, and retrieves default-dataset rows and this run’s hashed run-summary key-value record. Set APIFY_TOKEN in your shell; email is optional. If supplied, use an accurate, monitored address. When omitted, we use our own contact. SEC_CONTACT_NAME is optional and defaults to Plainpage Labs in this example. This starts a real hosted run and may incur the filing charge shown above. Do not blindly repeat the start request if its response is uncertain, since the run may already have started.
import hashlibimport jsonimport osimport timefrom urllib.error import HTTPErrorfrom urllib.request import Request, urlopenAPI = "https://api.apify.com/v2"ACTOR_ID = "plainpagelabs~sec-edgar-to-markdown"TOKEN = os.environ["APIFY_TOKEN"]def api(method, path, payload=None):body = None if payload is None else json.dumps(payload).encode("utf-8")request = Request(API + path,data=body,method=method,headers={"Authorization": f"Bearer {TOKEN}","Content-Type": "application/json",},)with urlopen(request, timeout=30) as response:return json.loads(response.read())input_data = {"tickers": [],"ciks": ["0000019617"],"accessionNumbers": ["0001628280-26-045167"],"formTypes": ["8-K"],"maxFilings": 1,"sections": [],"outputMode": "filings","includeExhibits": False,"includeFullMarkdown": False,"chunkSize": 0,"contactName": os.environ.get("SEC_CONTACT_NAME", "Plainpage Labs"),}contact_email = os.environ.get("SEC_CONTACT_EMAIL")if contact_email:input_data["contactEmail"] = contact_emailrun = api("POST", f"/acts/{ACTOR_ID}/runs", input_data)["data"]run_id = run["id"]deadline = time.monotonic() + 900while run["status"] in {"READY", "RUNNING", "STOPPING", "ABORTING", "TIMING-OUT"}:if time.monotonic() >= deadline:raise TimeoutError(f"Run is still active; inspect run {run_id} before taking action")time.sleep(3)run = api("GET", f"/actor-runs/{run_id}")["data"]dataset_id = run["defaultDatasetId"]store_id = run["defaultKeyValueStoreId"]items = api("GET", f"/datasets/{dataset_id}/items?format=json")summary_key = "RUN_SUMMARY-v1-" + hashlib.sha256(run_id.encode("utf-8")).hexdigest()[:20]try:summary = api("GET", f"/key-value-stores/{store_id}/records/{summary_key}")except HTTPError as error:if error.code != 404:raisesummary = Noneprint(json.dumps({"runId": run_id, "runStatus": run["status"],"items": items, "runSummary": summary}, indent=2))
Limitations and support
This Actor is not affiliated with or endorsed by the SEC. It is provided as-is; verify amounts against the original filing.
The parser can miss or misplace unusual filing layouts. complete and partial describe heuristic coverage, not financial accuracy. Cross-reference and multi-registrant gaps are reported when identified, but not every gap can be detected. Some older multi-company filings, including Southern Company's FY2006 10-K filed in 2007, return partial results with a warning identifying missing registrant discussions. Review the original filing before relying on returned text or amounts.
The Actor is designed to operate with Limited permissions and on the current run's default storage. It does not administer a user's account.
For help, use the Issues tab on the Actor page in Apify. The repository's GitHub issues are private and are not a public customer-support channel.
Output field reference
The dataset schema defines the versioned union of dataset rows. Every row requires schemaVersion: 1, a semantic-version appVersion, and recordType: filing, chunk, artifact, or diagnostic. RUN_SUMMARY is a separate key-value-store record with recordType: "run-summary"; it is not part of the dataset-row schema. Coverage and billing eligibility are different: a fully delivered useful partial filing may be billable, while a complete-looking parser result is not proof of financial accuracy. Compare important results with the original filing.
Dataset row types
recordType | Output mode | Shape |
|---|---|---|
filing | filings | One filing object with filing metadata and a sections array. The schema requires sections, filingContentHash, and filingChunkCount. Sections may carry Markdown and/or section-local chunks. A no-match filing still has an explicit status and coverage metadata. |
chunk | chunks | One dataset row per emitted chunk, with filing, section, selection, coverage, source and chunk metadata. The schema requires text, contentHash, filingContentHash, and filing-wide chunk index/count. One filing may create many rows. |
artifact | artifacts | One compact discovery row that identifies the verified full-filing JSON artifact in the run's default key-value store. The schema requires artifact, outputMode: "artifacts", filingContentHash, and filingChunkCount. The artifact object includes storeId, manifestKey, format, byte length, SHA-256 and part counts. artifact.sha256 covers the complete serialized archived payload, not the content-only filingContentHash. |
diagnostic | Lookup or processing issue | A status/error row for an issue that did not yield a normal filing record. It has schema/version metadata but does not receive an invented source URL or content hash. |
Each eligible filing accession can produce at most one billing event per run, independent of registrant count or dataset row count. A useful partial filing can be eligible after its selected output is completely delivered. Fetch/conversion failures, no-match, not-applicable-only output, and incomplete delivery are not charged. The billableContent field describes content eligibility; by itself it does not confirm a charge. See the run-level billing fields below and Pricing: $0.003 per filing.
Identity and content evidence
| Field | Meaning |
|---|---|
accessionNumber, cik, company, form, filingDate, reportDate, primaryDocument | Filing identity and filing metadata when known. |
url | Filing document URL used for retrieval. |
sourceUrl | URL of the section or exhibit source. It can differ from the filing's primary document when content was recovered from an exhibit. |
canonicalUrl | Optional deterministic SEC Archives URL for the primary document, derived only when the filing identity is known and CIK, accession, and document name pass validation. A diagnostic row may include it when that identity is complete; lookup failures without a known identity do not get a fabricated URL. It is not a substitute for section/exhibit sourceUrl. |
sourceHash | SHA-256 of the retrieved filing source text. In chunk rows it links the chunk back to the source filing; it is not a checksum of returned Markdown. |
contentHash | SHA-256 of section Markdown or an individual chunk's exact text, where present. |
filingContentHash | SHA-256 over a stable JSON projection of delivered text: sections in order with Item identity and Markdown/chunk text, optional top-level Markdown/full Markdown, and optional exhibits in order with present file, type, url, status, markdown, error, and omissionReason values. Whitespace and Unicode are significant. Derived metadata such as dates, warnings, and indices are excluded. Different chunk representations can yield different hashes when chunking repeats table headers. |
schemaVersion, appVersion, parserVersion | Output-contract version, Actor application version and filing parser version. Application and parser versions currently match (0.4.0); neither is a hosted-build identifier. |
markdown, sections[].markdown, sections[].chunks[].text | includeFullMarkdown:true adds full converted main-document text in top-level markdown. Selected section text is in sections[].markdown without chunking, or sections[].chunks[].text with chunking. |
In filings mode, filingChunkCount gives the total chunks across the filing's sections when chunking is enabled. In chunks mode, each chunk keeps its section-local chunkIndex/chunkCount and also has zero-based filingChunkIndex/filingChunkCount across the whole filing. These indices describe output order; they do not replace chunkId or identify source page ranges.
Other coverage evidence can include status, completeness, documentCompleteness, selectionStatus, expectedItems, missingItems, missingRequestedItems, partialRequestedItems, warnings, section contentStatus/detection, sourceCoverage/sourceCoverageScope, and index/registrant fields such as indexPageRanges, sourcePageRanges, missingSourcePageRanges, sourcePageLabels, missingSourcePageLabels, sourceRegistrant, sourceRegistrants, missingSourceRegistrants, and registrantSourceEvidence. Page-range fields use exactly two nonnegative integer endpoints in [first, last] arrays; they are not objects. sharedSourcePages maps source Item keys to the same pair format. Registrant evidence entries can include the filed name, index/source page labels, character-span evidence (sourceSpans entries with numeric start and end offsets), and a body hash. Source mapping can also include originalSourceItem, sourceContentRole, and overlap details in sharedContentWith and sharedSourcePages. These fields expose source ranges and known gaps; they do not certify correct extraction. Heuristic status labels report detected coverage, not an audited accuracy score.
For an artifact row, retrieve artifact.manifestKey from artifact.storeId in the run's default key-value store. The manifest lists compressed parts and checksums. Read all parts and verify them before using the reconstructed filing; the exported readFilingArtifact(store, manifestKey) helper performs these checks for SDK consumers. A missing or unreadable artifact is not a partial result to consume.
RUN_SUMMARY record
The Actor writes one cumulative reporting snapshot under the canonical key RUN_SUMMARY-v1- followed by the first 20 lowercase hexadecimal characters of SHA-256 over the UTF-8 actorRunId. After the canonical record is successfully written and read back, it also writes an exact snapshot copy to RUN_SUMMARY in the run's default key-value store; the run output reference links to this display alias. The hashed key remains authoritative and is used for per-run recovery. The alias is never used for recovery or billing. In a shared key-value store, a later run can overwrite the alias, so it may refer to a different or stale run; check actorRunId and read the canonical per-run key before relying on it. The summary is not atomic with the filing ledger or dataset writes. A bounded key-value timeout can stop the run with RUN_SUMMARY_UNAVAILABLE, but it does not cancel the underlying storage operation. A missing, stale or failed summary read/write is not proof of the ledger state; the filing ledger remains the authority for delivery and charging. The summary never authorizes delivery replay or a charge. Same-run restarts carry forward filing entries and increment executionCount.
To read it over the Apify API, derive the key from the run ID using RUN_SUMMARY-v1- plus sha256(actorRunId UTF-8)[:20]; the Python example above shows this calculation.
The record has this top-level structure:
| Field | Meaning |
|---|---|
recordType, schemaVersion, appVersion | run-summary, schema 1, and Actor application version 0.4.0. |
actorRunId | Apify run identity used to derive the per-run key and scope this summary. |
status, errorCode | running while work is active, then completed, failed, or killed; errorCode is the optional machine-readable failure code. |
startedAt, updatedAt, executionCount | ISO timestamps and number of executions recorded for this same run. |
selection | Input-plan summary: fingerprint, mode (explicit-accessions or latest-matching), maxFilings, requestedAccessionCount, tickerCount, cikCount, and planFrozen. The fingerprint is an identity check, not a user-readable selection list. |
counts | Accession-level requested, selected, complete, partial, unsupported, failed, skipped, and billed counts; these are not dataset-row counts. requested is the distinct explicit-accession count; for latest-matching selection it is null until the plan freezes, then reflects the selected plan. Outcome counts summarize recorded filings[] entries, which may include unselected discovery failures. billed is always null while running and remains null after a run if charge/accounting is uncertain; unknown is not zero. |
execution | Counts of skippedAlreadyDelivered and resumedCharges; these explain same-run recovery. |
billing | status, billed, confirmedBilled, sdkReportedCount, and reason. confirmedBilled is the lower bound of individually confirmed filing events. sdkReportedCount is a diagnostic aggregate, not a per-filing receipt. If the outcome or accounting cannot be established, status is unknown and billed remains null. |
filings | Per-accession results and evidence, one entry per accession rather than one per dataset row. |
discoveryIssues | Lookup/selection diagnostics with a code and message plus any known CIK, input, or accession. |
integrityHash | SHA-256 integrity checksum over the JSON record body without this field. It detects record changes; it does not certify source accuracy or platform billing. |
Each filings[] entry includes accessionNumber, selected, outcome, and reasons (code and message). Depending on processing and reconciliation it may also include cik, form, contentOutcome, contentHash, billableContent, billingReason, delivery, ledgerState, billed, and executionAction. Outcomes are complete, partial, unsupported, failed, skipped, or transient unknown. While work is in progress, a parsed result keeps its contentOutcome but its outcome remains unknown with OUTPUT_AWAITING_DELIVERY until the filing ledger confirms delivery. If the final ledger read is unavailable, outcome becomes failed, delivery becomes unknown, billingReason is LEDGER_STATE_UNKNOWN, and the parser result remains in contentOutcome. billed is true only for a confirmed event, false when not charged, and null when the result is uncertain. delivery is unstarted, confirmed, or unknown; executionAction explains a same-run skip or resumed charge when present. billingReason can identify NO_USABLE_CONTENT, NOT_APPLICABLE_ONLY, UNPRICED_RUN, DELIVERY_REQUIRED, AWAITING_FIRST_CHARGE, CONFIRMED_FILING_EVENT, CHARGE_OUTCOME_UNKNOWN, or LEDGER_STATE_UNKNOWN. OUTPUT_AWAITING_DELIVERY is a reason code; inspect each reason and the filing row's warnings/source evidence before interpreting a partial outcome.
billing.status: "confirmed" means the per-accession ledger and billing accounting are internally consistent; for a pay-per-event run, an available SDK event count must agree with the confirmed ledger charges. billing.status: "unknown" means not to infer the total from dataset rows or a partial aggregate; confirmedBilled remains the lower bound and billed stays null, not zero. sdkReportedCount is only an aggregate diagnostic, never a per-filing receipt. A delivery-pending record means delivery is uncertain, but it cannot have reached the charge-delivered transition: its per-filing delivery is unknown and billed is false. If the ledger and SDK both validate zero charged filings, the confirmed billing total can be zero even while that delivery issue still needs review. A charge-pending record may have reached the platform and makes billing unknown (billed: null); reconcile it through the documented support process, never by retrying from this summary. You pay $0.003 for each eligible filing successfully delivered ($3 per 1,000); Apify platform usage is included. The summary itself is not an Apify invoice.