Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.wokelo.ai/llms.txt

Use this file to discover all available pages before exploring further.

1. Overview

The Company Research API generates an in-depth intelligence report on any company, covering firmographics, financial signals, executive commentary, product portfolio, product launches, and M&A history. The report is fully source-cited and delivered as structured JSON or as a formatted document (PDF, DOCX, or PPT). This is an asynchronous POST API — submitting a request returns a report_id immediately. You then poll for completion using the Report Status endpoint and retrieve the finished report using the Download Report endpoint. The three-step workflow:
Step 1: POST /api/workflow_manager/start/         → returns report_id
Step 2: GET  /api/assets/get_notebook_status/     → poll until status = "Completed"
Step 3: POST /api/assets/download_report/         → retrieve JSON / PDF / DOCX / PPT
The completed report is structured into five major sections, each returned as a named key in the JSON output:
  • Quick Snapshot — firmographic card with financials, headcount, funding stage, HQ, and product summary
  • Executive Summary — multi-paragraph narrative covering strategy, recent developments, competitive dynamics, and key capabilities; fully source-cited
  • Key Insights — 8–10 analyst-style insight cards, each with a headline metric, supporting data paragraph, multi-point commentary, and source citations
  • Product Insights — product overview narrative, categorised product/service list, product launch timeline with summaries, and a screenshot
  • Transaction Highlights — M&A and investment map as structured data, plus a detailed acquisition timeline with deal amounts and strategic commentary
Common use cases:
  • Pre-meeting company briefings — generate a sourced, structured brief on any target or prospect before a call, in minutes rather than hours
  • Deal screening packages — run Company Research on a shortlist of acquisition targets and export as PDF or PPT for IC review
  • Portfolio company monitoring — schedule quarterly runs on portfolio companies and diff the Key Insights across periods to track strategic shifts
  • Competitive intelligence — run against a set of competitors and compare product portfolios and M&A timelines side by side
  • CRM enrichment — ingest the JSON output and push firmographic and financial fields into Salesforce, HubSpot, or DealCloud records automatically
  • Custom report augmentation — supply custom_files to layer proprietary internal data (CIM, management presentation, diligence notes) on top of Wokelo’s synthesis
This API is asynchronous. The initial POST returns a report_id only — not the report content. Report generation typically completes in 2–5 minutes. See How Async APIs work for a full explanation of the polling lifecycle.

2. Quick Start

Step 1 — Submit the report request
curl --location 'https://api.wokelo.ai/api/workflow_manager/start/' \
  --header 'Authorization: Bearer <YOUR_API_TOKEN>' \
  --header 'Content-Type: application/json' \
  --data '{
    "workflow": "company_primer",
    "permalink": "salesforce"
  }'
Step 2 — Poll for completion
import time

def wait_for_report(report_id, api_key, poll_interval=15, timeout=600):
    headers = {"Authorization": f"Bearer {api_key}"}
    elapsed = 0
    while elapsed < timeout:
        r = requests.get(
            "https://api.wokelo.ai/api/assets/get_notebook_status/",
            headers=headers,
            params={"report_id": report_id}
        )
        status = r.json().get("status", "")
        print(f"[{elapsed}s] Status: {status}")
        if status == "Completed":
            return True
        if status == "Failed":
            raise Exception(f"Report {report_id} failed")
        time.sleep(poll_interval)
        elapsed += poll_interval
    raise TimeoutError(f"Report {report_id} did not complete within {timeout}s")

wait_for_report(report_id, api_key="<YOUR_API_TOKEN>")
Step 3 — Download the report
# Fetch as JSON for programmatic processing
result = requests.post(
    "https://api.wokelo.ai/api/assets/download_report/",
    headers={"Authorization": "Bearer <YOUR_API_TOKEN>"},
    json={"report_id": report_id, "file_type": "json"}
)
report = result.json()

# Or download as a formatted document
pdf_result = requests.post(
    "https://api.wokelo.ai/api/assets/download_report/",
    headers={"Authorization": "Bearer <YOUR_API_TOKEN>"},
    json={"report_id": report_id, "file_type": "pdf"}
)
with open("company_research.pdf", "wb") as f:
    f.write(pdf_result.content)

3. Authentication

All requests must include a Bearer token in the Authorization HTTP header.
Authorization: Bearer <YOUR_API_TOKEN>
API tokens are issued from your Wokelo account. Navigate to Account Details → API Credentials in the Wokelo dashboard to get your client id and client secret. Contact support@wokelo.ai if you do not yet have API access.
Never expose your token in client-side code, browser requests, or public repositories. A missing or invalid token returns 401 Unauthorized. A valid token without sufficient plan permissions returns 403 Forbidden.

4. Request Reference

Endpoint
POST https://api.wokelo.ai/api/workflow_manager/start/
All parameters are passed as JSON in the request body.
ParameterTypeRequiredDescription
workflowstringRequiredMust always be "company_primer". This fixed value tells Wokelo’s workflow engine to run the Company Research report.
permalinkstringRequired*Wokelo/Crunchbase permalink of the company (e.g. "salesforce", "tesla-motors"). Use the Company Search API to look up a company’s permalink by name.
websitestringRequired*Full URL of the company’s website (e.g. "https://www.salesforce.com"). Accepted as an alternative to permalink when the permalink is unknown.
industrystringOptionalCompany’s industry vertical. If omitted, Wokelo auto-detects the industry from its knowledge base. Providing this speeds up processing and can improve industry-specific framing in the report.
workbook_namestringOptionalLabel for the generated report workbook. Defaults to the company name if omitted.
custom_filesobject[]OptionalArray of file references to include in the report alongside Wokelo’s synthesis. Each object should use the fileName value returned by the File Upload API. Use this to incorporate CIMs, management presentations, or internal diligence notes.
Either permalink or website is required — at least one must be provided. If both are supplied, permalink takes precedence. If you cannot find a company’s permalink via Company Search, pass the full company website URL instead.
Full request example:
curl --location 'https://api.wokelo.ai/api/workflow_manager/start/' \
  --header 'Authorization: Bearer <YOUR_API_TOKEN>' \
  --header 'Content-Type: application/json' \
  --data '{
    "workflow": "company_primer",
    "permalink": "salesforce",
    "industry": "Enterprise Software & Cloud",
    "workbook_name": "Salesforce Q2 2026 Research"
  }'

5. Response

Submission response

The initial POST returns immediately with a single field:
{
  "report_id": 123345
}
FieldTypeDescription
report_idintegerUnique identifier for this report job. Use it with the Report Status and Download Report endpoints to track and retrieve the completed report. Store this value — it is the only handle to your report.

Report status response

Poll GET /api/assets/get_notebook_status/?report_id={report_id} until the status field is "Completed".
Status valueMeaning
"Pending"Report is queued and waiting to start.
"Processing"Report generation is in progress.
"Completed"Report is ready to download.
"Failed"Report generation encountered an error. Retry the submission.

Downloaded report structure

When you call Download Report with "file_type": "json", the response is a deeply nested JSON object. Each top-level key is a named report section. The five sections produced by Company Research are: Quick Snapshot Contains a quick_snapshot object with two sub-objects:
  • company_firmographics — organisation name, logo, dashboard URL, and a details object covering: industry, founded_year, website, hq, operating_status, company_type, employees_in_crunchbase, employees_in_linkedin, last_funding_round, ticker, and a financials object with revenue, ebitda, pat, and market_cap (each with currency, value, and period fields)
  • product_detailsproduct_category (short label) and summary (one-paragraph product description)
Executive Summary Contains a summary field with a full markdown-formatted multi-section narrative (Overview, Strategic Positioning, Recent Developments, Competitive Dynamics, Key Capabilities), plus a source array of citation objects, each with id, title, url, publisher, and date. Key Insights Contains a key_insights array of 8–10 insight card objects. Each card has:
  • insight — object with title (bold headline with key metric), paragraph (supporting data sentence), and sources array
  • commentary — array of 2–3 commentary paragraph objects, each with paragraph (analyst interpretation) and sources array
Product Insights Contains two sub-sections:
  • Product and servicesproduct_overview (summary narrative + screenshot_url) and products_and_services (array of product area objects, each with product_area label, details array of {title, summary} objects, and sources citation range)
  • Product launches and initiativesreleased_products array of launch objects, each with date, initiative, summary (bullet array), and sources array of {url, title, publisher, publishedDate} objects; and future_announcements array
Transaction Highlights Contains two sub-sections:
  • M&A and Investments mapm_andn_a_map with a charts object containing m_and_a_investment_map, which has a data array of deal objects (year, company_name, product_category, headquarters, permalink, website, type as "mna" or "investment", plus parent company fields), a source attribution string, and a url to the chart image
  • M&A Strategyacquisitions with m_and_a_timeline array of detailed acquisition objects, each with a nested company card (firmographics + description), acquisition_date, acquisition_amount (numeric USD or empty string), and commentary
The acquisition_amount field is a raw numeric USD value (e.g. 1900000000.0 for $1.9B) or an empty string when the deal value was not publicly disclosed. Always check for empty string before converting to currency.

Download format options

The file_type parameter in the Download Report request controls the output format.
file_typeDescription
"json"Fully structured JSON — all sections, fields, and source citations. Best for programmatic processing, CRM ingestion, or downstream pipelines.
"pdf"Formatted PDF report with charts, tables, and narrative sections. Best for sharing with stakeholders.
"docx"Editable Word document. Best for teams that customise reports before distribution.
"ppt"PowerPoint presentation with section slides. Best for IC presentations and LP updates.

6. Examples

The most common pattern: look up the permalink, submit, poll, and download JSON.
# Step 1: Submit
curl --location 'https://api.wokelo.ai/api/workflow_manager/start/' \
  --header 'Authorization: Bearer <YOUR_API_TOKEN>' \
  --header 'Content-Type: application/json' \
  --data '{"workflow": "company_primer", "permalink": "salesforce"}'

# Step 2: Poll (replace 123345 with your report_id)
curl --location 'https://api.wokelo.ai/api/assets/get_notebook_status/?report_id=123345' \
  --header 'Authorization: Bearer <YOUR_API_TOKEN>'

# Step 3: Download
curl --location 'https://api.wokelo.ai/api/assets/download_report/' \
  --header 'Authorization: Bearer <YOUR_API_TOKEN>' \
  --header 'Content-Type: application/json' \
  --data '{"report_id": 123345, "file_type": "json"}'
Sample submission response:
{
  "report_id": 123345
}
When the company’s permalink is unknown or unavailable, pass the full website URL.
response = requests.post(
    "https://api.wokelo.ai/api/workflow_manager/start/",
    headers=HEADERS,
    json={
        "workflow": "company_primer",
        "website": "https://www.stripe.com"
    }
)
report_id = response.json()["report_id"]
Use the Company Search API to find a company’s permalink by name, then pass it to Company Research.
# Search for the company
search = requests.get(
    "https://api.wokelo.ai/api/enterprise/company/search",
    headers=HEADERS,
    params={"query": "HubSpot", "search_by": "name", "company_type": "all"}
)
permalink = search.json()["data"][0]["permalink"]  # e.g. "hubspot"

# Submit research report
submit = requests.post(
    "https://api.wokelo.ai/api/workflow_manager/start/",
    headers=HEADERS,
    json={"workflow": "company_primer", "permalink": permalink}
)
report_id = submit.json()["report_id"]
print(f"Research for {permalink} started. report_id: {report_id}")

Batch research on a watchlist

Submit Company Research for multiple companies concurrently, then collect all reports once completed.
import requests, time, concurrent.futures

API_KEY  = "<YOUR_API_TOKEN>"
HEADERS  = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}

COMPANIES = ["salesforce", "hubspot", "pipedrive", "zoho", "freshworks"]

def submit_research(permalink):
    r = requests.post(
        "https://api.wokelo.ai/api/workflow_manager/start/",
        headers=HEADERS,
        json={"workflow": "company_primer", "permalink": permalink}
    )
    return permalink, r.json()["report_id"]

def poll_until_done(report_id, interval=20, timeout=900):
    elapsed = 0
    while elapsed < timeout:
        r = requests.get(
            "https://api.wokelo.ai/api/assets/get_notebook_status/",
            headers=HEADERS,
            params={"report_id": report_id}
        )
        status = r.json().get("status")
        if status == "Completed":
            return True
        if status == "Failed":
            return False
        time.sleep(interval)
        elapsed += interval
    return False

def fetch_report(report_id):
    r = requests.post(
        "https://api.wokelo.ai/api/assets/download_report/",
        headers=HEADERS,
        json={"report_id": report_id, "file_type": "json"}
    )
    return r.json()

# Submit all concurrently
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as pool:
    futures = {pool.submit(submit_research, c): c for c in COMPANIES}
    jobs = {}
    for future in concurrent.futures.as_completed(futures):
        permalink, report_id = future.result()
        jobs[permalink] = report_id
        print(f"Submitted: {permalink} → report_id {report_id}")

# Poll and collect
reports = {}
for permalink, report_id in jobs.items():
    ok = poll_until_done(report_id)
    if ok:
        reports[permalink] = fetch_report(report_id)
        print(f"Collected: {permalink}")
    else:
        print(f"Failed or timed out: {permalink}")

print(f"\nCollected {len(reports)} / {len(COMPANIES)} reports")

Research with a custom file (CIM or management deck)

Upload a proprietary file and attach it to the report so Wokelo synthesises it alongside its own research.
import requests

API_KEY = "<YOUR_API_TOKEN>"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}

# Step 1: Upload the file
with open("target_company_cim.pdf", "rb") as f:
    upload_r = requests.post(
        "https://api.wokelo.ai/api/assets/upload/",
        headers=HEADERS,
        files={"files": ("target_company_cim.pdf", f, "application/pdf")}
    )
file_name = upload_r.json()["fileName"]  # unique file identifier

# Step 2: Submit report referencing the uploaded file
submit_r = requests.post(
    "https://api.wokelo.ai/api/workflow_manager/start/",
    headers={**HEADERS, "Content-Type": "application/json"},
    json={
        "workflow":     "company_primer",
        "permalink":    "target-company-permalink",
        "custom_files": [{"fileName": file_name}]
    }
)
report_id = submit_r.json()["report_id"]
print(f"Report with custom file submitted. report_id: {report_id}")

Extracting specific sections from the JSON report

Work with individual sections of the downloaded JSON for targeted downstream processing.
# Assume report is already downloaded as a dict
report = fetch_report(report_id)

# --- Quick Snapshot ---
snapshot   = report["Quick Snapshot"]["Quick Snapshot"]["quick_snapshot"]
firmograph  = snapshot["company_firmographics"]["details"]
financials  = firmograph["financials"]

print(f"Company:    {snapshot['company_firmographics']['organization']['name']}")
print(f"HQ:         {firmograph['hq']}")
print(f"Employees:  {firmograph['employees_in_linkedin']:,}")
print(f"Revenue:    ${financials['revenue']['value'] / 1e9:.1f}B ({financials['revenue']['period']})")
print(f"Market Cap: ${financials['market_cap']['value'] / 1e9:.1f}B")

# --- Key Insights (headlines only) ---
insights = report["Executive Summary"]["Key Insights"]["key_insights"]
print(f"\nKey Insights ({len(insights)} total):")
for i, card in enumerate(insights, 1):
    print(f"  {i}. {card['insight']['title']}")

# --- Recent M&A (2024+) ---
timeline = report["Transaction highlights"]["M&A Strategy"]["acquisitions"]["m_and_a_timeline"]
recent_ma = [t for t in timeline if int(t["acquisition_date"][:4]) >= 2024]
print(f"\nAcquisitions since 2024: {len(recent_ma)}")
for t in recent_ma:
    amt = t["acquisition_amount"]
    amt_str = f"${amt / 1e9:.1f}B" if isinstance(amt, float) and amt > 0 else "undisclosed"
    print(f"  {t['acquisition_date'][:10]}  {t['company']['organization']['name']}  ({amt_str})")

# --- Product areas ---
products = report["Product insights"]["Product and services"]["products_and_services"]
print(f"\nProduct areas ({len(products)}):")
for area in products:
    titles = [p["title"] for p in area["details"]]
    print(f"  {area['product_area']}: {', '.join(titles)}")

Exporting a PPT for IC review

import requests

HEADERS = {"Authorization": "Bearer <YOUR_API_TOKEN>"}

ppt_r = requests.post(
    "https://api.wokelo.ai/api/assets/download_report/",
    headers=HEADERS,
    json={"report_id": 123345, "file_type": "ppt"}
)

with open("salesforce_research.pptx", "wb") as f:
    f.write(ppt_r.content)

print("Saved salesforce_research.pptx")

7. Error Handling

The API uses standard HTTP status codes. The submission endpoint returns errors synchronously; processing errors appear as a "Failed" status when polling.
StatusMeaningCause & Resolution
200 OKRequest acceptedreport_id returned. Proceed to polling.
400 Bad RequestInvalid parametersMissing workflow value, missing both permalink and website, or an unrecognised workflow string. Check the detail field.
401 UnauthorizedAuth failedThe Authorization header is missing or contains an invalid token. Verify your key in Settings → API Keys.
403 ForbiddenInsufficient accessYour plan does not include access to this endpoint or to this company. Contact support@wokelo.ai.
404 Not FoundCompany not foundThe permalink could not be resolved. Use the Company Search API to verify, or switch to website.
429 Too Many RequestsRate limit exceededImplement exponential back-off on submission. The response includes a Retry-After header.
500 Internal Server ErrorServer errorRetry the submission after a brief delay. If the issue persists, contact support@wokelo.ai.
Handling a "Failed" report status:
status_r = requests.get(
    "https://api.wokelo.ai/api/assets/get_notebook_status/",
    headers=HEADERS,
    params={"report_id": report_id}
)
data = status_r.json()

if data.get("status") == "Failed":
    print(f"Report {report_id} failed.")
    # Resubmit with the same parameters
    resubmit = requests.post(
        "https://api.wokelo.ai/api/workflow_manager/start/",
        headers=HEADERS,
        json={"workflow": "company_primer", "permalink": "salesforce"}
    )
    new_report_id = resubmit.json()["report_id"]
    print(f"Resubmitted. New report_id: {new_report_id}")
Retry with exponential back-off on submission:
import time, requests

def submit_with_retry(body, api_key, max_retries=3):
    headers = {
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json"
    }
    for attempt in range(max_retries):
        try:
            r = requests.post(
                "https://api.wokelo.ai/api/workflow_manager/start/",
                headers=headers,
                json=body,
                timeout=30
            )
            if r.status_code == 429:
                time.sleep(2 ** attempt)
                continue
            r.raise_for_status()
            return r.json()["report_id"]
        except requests.exceptions.Timeout:
            if attempt == max_retries - 1:
                raise
            time.sleep(1)
    raise Exception(f"Submission failed after {max_retries} attempts")

8. Best Practices

Always use permalink over website when available Permalinks resolve faster and more reliably than website URLs, particularly for companies with complex domain structures or redirects. Use the Company Search API to resolve a company name to its permalink before submitting:
search = requests.get(
    "https://api.wokelo.ai/api/enterprise/company/search",
    headers=HEADERS,
    params={"query": "OpenAI", "search_by": "name"}
)
permalink = search.json()["data"][0]["permalink"]
workflow must always be "company_primer" — this field is not optional The /api/workflow_manager/start/ endpoint is shared across all Workflow APIs (Company Research, Industry Research, Peer Comparison, Custom). The workflow parameter is what distinguishes between them. For Company Research, it must be exactly "company_primer". Omitting it or misspelling it returns a 400 error. Store report_id immediately and durably The submission response contains only report_id. If this value is lost before the report is downloaded, there is no API endpoint to list or retrieve past report_id values. Store it to a database, log, or queue entry as soon as the submission response is received. Use 15–20 second polling intervals — not tight loops Reports typically complete in 2–5 minutes. Polling more frequently than every 15 seconds wastes rate limit budget without meaningfully reducing latency. Use a back-off strategy for batch workloads:
# Recommended polling pattern
for wait in [15, 15, 30, 30, 60, 60, 120]:   # seconds
    time.sleep(wait)
    status = get_status(report_id)
    if status in ("Completed", "Failed"):
        break
Provide industry to improve report framing for niche companies Wokelo auto-detects industry from its knowledge base, but for newer companies, international companies, or firms in niche verticals, providing the industry string directly improves the accuracy of competitive framing and peer positioning within the Executive Summary and Key Insights:
json={
    "workflow":   "company_primer",
    "permalink":  "some-niche-startup",
    "industry":   "Climate Technology & Carbon Markets"
}
Use custom_files to layer internal data on public synthesis The custom_files parameter is the most differentiated capability of this API for investment workflows. Uploading a CIM, management presentation, or prior diligence memo alongside the request causes Wokelo to synthesise both the public web research and your internal documents into a unified report. Upload files via the File Upload API first, then reference the returned fileName in your request. Parse acquisition_amount defensively — it can be an empty string or a float The acquisition_amount field in the M&A timeline is either a numeric float (raw USD) or an empty string "" for undisclosed deals. Never assume it is numeric:
# ❌ Will throw TypeError when amount is ""
formatted = f"${deal['acquisition_amount'] / 1e9:.1f}B"

# ✅ Safe — check type first
amt = deal.get("acquisition_amount")
formatted = f"${amt / 1e9:.1f}B" if isinstance(amt, (int, float)) and amt > 0 else "Undisclosed"
Use "json" output for pipelines; "ppt" or "pdf" for human deliverables The JSON output contains every field, source citation, and structured data object. Use it for any downstream processing — CRM ingestion, comparison tools, alerting. The PDF, DOCX, and PPT outputs are rendered for human consumption and strip much of the structured metadata. Request only the format you need — a single report_id supports all four formats.

Company Instant Enrichment

Synchronous firmographic enrichment — returns structured company data immediately without a report generation step.

Company Deep Intelligence

Deeper async intelligence on strategy, financials, and competitive positioning — more granular than Company Research.

Industry Research

Run the same async workflow pattern against an industry or sector instead of a specific company.

Peer Comparison

Generate a structured comparison of two or more companies across key dimensions in a single report.

Company News Monitoring

Fetch real-time, source-cited news articles for any company — synchronous, no polling required.

Supporting APIs

Company Search, File Upload, Report Status, and Download Report — all used alongside Company Research.