StartupGraph in Production: A Self-Hosted Startup Intelligence System
StartupGraph is a working product. Its current implementation is called EntityScope: a self-hosted startup knowledge system that gives agents structured, sourced answers without requiring a dashboard.
The useful lesson is not that a small database can “replace Crunchbase.” It cannot. The lesson is that a focused intelligence product can replace a specific workflow: opening several sources, copying facts into notes, and losing track of where each value came from.
This is a case study of the system that exists today—code, database, query tools, tests, and limitations—not a description of a hypothetical prediction engine.
What “working” means
A product is more than an architecture diagram. StartupGraph has an embedded database, repeatable ingestion, field-level provenance, deterministic query filters, an agent-facing MCP server, freshness checks, and a write-back path to research notes.
On July 28, 2026, I inspected the local product snapshot in read-only mode:
| Dataset | Rows | Role |
|---|---|---|
| Legal entities | 1,010 | Canonical company spine |
| Field provenance | 8,018 | Source, confidence, and verification time |
| Prospect profiles | 479 | Funding, stage, team, location, and tags |
| Outreach records | 394 | Curated company and contact research |
Those are snapshot counts, not marketing totals. The database changes as sources are refreshed and research is added.
The problem: synthesis, not storage
The original workflow needed five tabs to answer one question. A registry could confirm a legal entity. Research notes held funding and team context. Wikidata contributed a small set of public facts. CORDIS could add EU-funded research projects. GitHub could expose public engineering activity. None used the same identifiers or freshness model.
Five Whys led to the actual product requirement:
- Why was startup research slow? Each answer required manual source switching.
- Why did source switching persist? Every source used a different schema.
- Why could the schemas not simply be joined? Names drift, entities share names, and many records lack a stable cross-source identifier.
- Why were merged answers hard to trust? Earlier notes stored values without field-level provenance.
- Why build StartupGraph? Agents needed one query surface that preserved the evidence behind every answer.
The enemy was never a particular vendor. It was the copy-and-paste research loop.
The architecture that survived contact with reality
Research sources
├── Berlin business data
├── curated prospect profiles
├── Wikidata
├── CORDIS integration
└── GitHub integration
│
▼
ingest → resolve entity → store value + provenance
│
▼
DuckDB: entities / entity_sources / prospects
│
▼
structured filters → parameterized SQL → sourced result
│
▼
MCP tools used by the research agent
DuckDB fits this workload because it runs analytical SQL inside the application process. There is no separate database service to operate for a single-user research system.
The agent boundary uses the Model Context Protocol. The product exposes four narrow tools:
entityscope_querytranslates a natural-language request into validated filters.entityscope_get_entityreturns a complete profile by stable ID or slug.entityscope_list_sourcesreports available sources and freshness.entityscope_healthreports counts and staleness.
The LLM never writes SQL. It may translate a request into a typed filter object; the application then builds parameterized SQL. Common phrases are handled by a deterministic parser first, so “small software companies in Berlin” does not require an API call.
Provenance is the product
A master company record is convenient. A master record with no evidence is a confident-looking liability.
StartupGraph stores provenance at field level:
{
"entity_id": "HRB-123456",
"field": "employee_count",
"value": 42,
"source": "berlin_dataset",
"confidence": 0.9,
"last_verified": "2026-05-27T18:04:54Z"
}
This makes conflicts visible. A manually reviewed value can outrank a bulk source without erasing it. An agent can reject a stale field or route a low-confidence answer for review. The rule is conservative: missing a match is cheaper than silently merging two different companies.
What the current snapshot can—and cannot—claim
The codebase includes ingestion modules for public GitHub data and CORDIS open data, plus Wikidata enrichment using its documented data-access interfaces. Implemented integration code is not the same as populated production data.
| Capability | Status in inspected snapshot | Safe claim |
|---|---|---|
| Berlin entity spine | 8,000 provenance rows | Populated |
| Wikidata enrichment | 16 provenance rows | Populated, limited coverage |
| Web/Wikipedia research | 2 provenance rows | Populated, limited coverage |
| CORDIS and GitHub modules | Code and tests exist; no provenance rows in this snapshot | Implemented, not yet populated here |
| Predictive funding or momentum scores | No validated model in this snapshot | Do not claim prediction |
This distinction matters. Hiring, grant, funding, and engineering activity can become useful signals, but a signal is not a forecast until it is tested against point-in-time data and a negative cohort. StartupGraph is working intelligence infrastructure; it is not a machine that sees the future.
A query contract agents can depend on
The query layer accepts dimensions such as city, industry, employee range, funding stage, funding range, founding year, tags, and company name. It caps result size and validates LLM output before execution.
Question:
"Show small Berlin software companies founded since 2020"
Validated filters:
{
"city": "Berlin",
"industry_label": "Computer",
"employees_on_site_min": 1,
"employees_on_site_max": 10,
"founding_year_min": 2020,
"limit": 20
}
The important artifact is the filter object. It creates a reviewable boundary between ambiguous language and deterministic data access. When a result looks wrong, the operator can inspect the interpretation before blaming the database.
Production evidence includes failed assertions
During this review, 130 tests passed and one integration test failed. The failing test hard-coded an old expectation of 100 prospect files; the current directory contains 479. That is not a data-engine failure, but it is real maintenance debt: corpus-size tests should verify invariants, not freeze a growing dataset at an obsolete count.
This is worth publishing because “working” should not mean “perfect.” It should mean the product is executable, observable, testable, and honest about known gaps.
Lessons from operating a small intelligence product
- Start with a decision, not a universe. Define the research question the graph must answer before adding another source.
- Track evidence per field. Record-level source labels are too coarse when one profile mixes registry, manual, and public-web facts.
- Separate ingestion from interpretation. Raw facts, derived signals, and business decisions need different tables and review rules.
- Prefer deterministic filters. Natural language is an interface; parameterized queries remain the execution contract.
- Measure freshness explicitly. A weekly check treats data age as an operational condition instead of a footnote.
- Do not confuse adapters with coverage. A source connector is not valuable until it produces verified rows.
Should you build one?
Build a focused graph when your questions repeatedly cross sources, your research produces proprietary annotations, and you need an agent to return evidence—not merely an answer. Buy access to a commercial dataset when broad coverage, analyst verification, and continuous updates matter more than workflow control.
Most teams should use a hybrid: buy broad reference data, preserve proprietary research and provenance locally, and expose only the narrow query contract their agents need. I cover that decision separately in the build-vs-buy guide; the lower-level schema and MCP design are documented in the agent-first entity database article.
Use the evidence checklist before choosing a commercial dataset or building your own startup graph.
Compare Build vs Buy