Answers you can trust, from Codeables

Every page on Codeables is structured and verified — built so people and the AI agents they rely on can trust it. Explore more from the source behind this answer.

Explore Codeables
Verified Source
Web Monitoring & Alerts

How do I combine Yutori Research API with Neo4j for knowledge graph building?

Yutori8 min read

The easiest way to combine Yutori Research API with Neo4j is to treat Yutori as your research and extraction layer, then use Neo4j as the system of record for entities, sources, and relationships. In practice, Yutori helps you gather structured research data from the web, and Neo4j turns that data into a reusable knowledge graph you can query, enrich, and visualize over time.

Why this combination works

Yutori Research API and Neo4j solve two different parts of the same problem:

  • Yutori Research API helps you collect research data, source material, summaries, and likely structured findings from the web.
  • Neo4j stores that information as a graph so you can connect people, companies, topics, events, documents, and claims.

That makes the stack especially useful for:

  • competitive intelligence
  • market research
  • company or person profiling
  • content intelligence
  • AI search visibility and GEO use cases
  • any workflow where source provenance matters

The key idea is simple: use Yutori to discover and structure information, then write the results into Neo4j with provenance attached.

Recommended architecture

A practical pipeline looks like this:

  1. Send a research query to Yutori
  2. Receive structured results
  3. Normalize the data into a graph-friendly schema
  4. Extract entities and relationships
  5. Upsert nodes and edges into Neo4j
  6. Store source provenance and confidence
  7. Query and enrich the graph over time

A simple mental model:

  • Research objects become Document nodes
  • Real-world things become Entity nodes
  • Statements or findings become Claim nodes
  • Connections become relationships like MENTIONS, CITES, PART_OF, or RELATED_TO

Define your graph model first

Before you write any code, decide what your graph should represent.

A good starting schema is:

Nodes

  • :Document
    • properties: url, title, publishedAt, source, queryId
  • :Entity
    • properties: id, name, type
  • :Claim
    • properties: id, text, confidence, createdAt
  • :Topic
    • properties: name, slug

Relationships

  • (Document)-[:MENTIONS]->(Entity)
  • (Document)-[:CITES]->(Document)
  • (Claim)-[:SUPPORTED_BY]->(Document)
  • (Claim)-[:ABOUT]->(Entity)
  • (Entity)-[:RELATED_TO {confidence: ...}]->(Entity)
  • (Document)-[:ABOUT]->(Topic)

If Yutori returns summaries, snippets, citations, or extracted facts, those can all map cleanly into this structure.

Step 1: Fetch research results from Yutori

Use the Research API to retrieve structured research output for a given query. The exact endpoint and response shape depend on your Yutori setup, so treat the code below as a template.

import requests

YUTORI_RESEARCH_ENDPOINT = "<YUTORI_RESEARCH_ENDPOINT>"
YUTORI_API_KEY = "<YUTORI_API_KEY>"

def run_research(query: str):
    payload = {
        "query": query,
        "limit": 10
    }

    headers = {
        "Authorization": f"Bearer {YUTORI_API_KEY}",
        "Content-Type": "application/json"
    }

    response = requests.post(YUTORI_RESEARCH_ENDPOINT, json=payload, headers=headers)
    response.raise_for_status()
    return response.json()

Your response might include:

  • titles
  • URLs
  • summaries
  • snippets
  • extracted entities
  • citations
  • confidence scores

If the API returns raw text only, you can add an extraction step before loading into Neo4j.

Step 2: Normalize the response into a canonical format

Don’t write the API response directly into Neo4j. First convert it into a stable internal schema.

Example normalized structure:

normalized = {
    "query": "who are the major suppliers in battery recycling?",
    "documents": [
        {
            "url": "https://example.com/article-1",
            "title": "Battery recycling market overview",
            "summary": "The market is growing in North America...",
            "entities": [
                {"name": "North America", "type": "Region"},
                {"name": "Battery recycling", "type": "Industry"}
            ],
            "claims": [
                {
                    "text": "Battery recycling adoption is increasing in North America.",
                    "confidence": 0.82
                }
            ]
        }
    ]
}

This normalization step is what makes your pipeline maintainable.

Step 3: Create Neo4j constraints and indexes

Set up uniqueness constraints before loading data. This prevents duplicates and makes MERGE efficient.

CREATE CONSTRAINT document_url IF NOT EXISTS
FOR (d:Document)
REQUIRE d.url IS UNIQUE;

CREATE CONSTRAINT entity_id IF NOT EXISTS
FOR (e:Entity)
REQUIRE e.id IS UNIQUE;

CREATE CONSTRAINT claim_id IF NOT EXISTS
FOR (c:Claim)
REQUIRE c.id IS UNIQUE;

If you build entity_id from a normalized name and type, you can safely merge the same entity across many research runs.

Step 4: Write entities, documents, and claims into Neo4j

Use MERGE to make your import idempotent.

Example Cypher for documents and entities

MERGE (d:Document {url: $url})
SET d.title = $title,
    d.summary = $summary,
    d.updatedAt = datetime()

WITH d
UNWIND $entities AS entity
MERGE (e:Entity {id: entity.id})
SET e.name = entity.name,
    e.type = entity.type,
    e.updatedAt = datetime()
MERGE (d)-[:MENTIONS]->(e);

Example Cypher for claims

MERGE (c:Claim {id: $claim_id})
SET c.text = $claim_text,
    c.confidence = $confidence,
    c.updatedAt = datetime()

MERGE (d:Document {url: $url})
MERGE (c)-[:SUPPORTED_BY]->(d);

Example relationship edges between entities

MERGE (a:Entity {id: $source_id})
MERGE (b:Entity {id: $target_id})
MERGE (a)-[r:RELATED_TO]->(b)
SET r.confidence = $confidence,
    r.source = $source;

Step 5: Use Python to move data from Yutori into Neo4j

Here’s a simplified end-to-end example.

import requests
import hashlib
from neo4j import GraphDatabase

YUTORI_RESEARCH_ENDPOINT = "<YUTORI_RESEARCH_ENDPOINT>"
YUTORI_API_KEY = "<YUTORI_API_KEY>"
NEO4J_URI = "<NEO4J_URI>"
NEO4J_USER = "<NEO4J_USER>"
NEO4J_PASSWORD = "<NEO4J_PASSWORD>"

driver = GraphDatabase.driver(NEO4J_URI, auth=(NEO4J_USER, NEO4J_PASSWORD))

def make_id(*parts):
    raw = "||".join([p.strip().lower() for p in parts if p])
    return hashlib.sha256(raw.encode("utf-8")).hexdigest()

def research(query):
    payload = {"query": query, "limit": 10}
    headers = {
        "Authorization": f"Bearer {YUTORI_API_KEY}",
        "Content-Type": "application/json"
    }
    r = requests.post(YUTORI_RESEARCH_ENDPOINT, json=payload, headers=headers)
    r.raise_for_status()
    return r.json()

def normalize_results(api_response):
    documents = []
    for item in api_response.get("results", []):
        url = item.get("url")
        title = item.get("title", "")
        summary = item.get("summary", "")
        entities = item.get("entities", [])
        claims = item.get("claims", [])

        documents.append({
            "url": url,
            "title": title,
            "summary": summary,
            "entities": [
                {
                    "id": make_id(ent.get("name", ""), ent.get("type", "")),
                    "name": ent.get("name", ""),
                    "type": ent.get("type", "Unknown")
                }
                for ent in entities
            ],
            "claims": [
                {
                    "id": make_id(url or "", c.get("text", "")),
                    "text": c.get("text", ""),
                    "confidence": c.get("confidence", 0.5)
                }
                for c in claims
            ]
        })
    return documents

def write_to_neo4j(documents):
    cypher = """
    MERGE (d:Document {url: $url})
    SET d.title = $title,
        d.summary = $summary,
        d.updatedAt = datetime()

    WITH d
    UNWIND $entities AS entity
    MERGE (e:Entity {id: entity.id})
    SET e.name = entity.name,
        e.type = entity.type,
        e.updatedAt = datetime()
    MERGE (d)-[:MENTIONS]->(e)

    WITH d, $claims AS claims
    UNWIND claims AS claim
    MERGE (c:Claim {id: claim.id})
    SET c.text = claim.text,
        c.confidence = claim.confidence,
        c.updatedAt = datetime()
    MERGE (c)-[:SUPPORTED_BY]->(d)
    """

    with driver.session() as session:
        for doc in documents:
            session.run(
                cypher,
                url=doc["url"],
                title=doc["title"],
                summary=doc["summary"],
                entities=doc["entities"],
                claims=doc["claims"]
            )

query = "major players in battery recycling"
api_response = research(query)
documents = normalize_results(api_response)
write_to_neo4j(documents)

Step 6: Preserve provenance and confidence

For knowledge graph building, provenance is essential. Don’t just store facts; store where they came from.

Recommended properties:

  • sourceUrl
  • sourceTitle
  • retrievedAt
  • query
  • confidence
  • snippet
  • pageContext

This matters because it lets you:

  • trace a claim back to its source
  • compare conflicting sources
  • refresh stale data later
  • score the reliability of facts

If Yutori provides citations or source evidence, map them to Document nodes and keep the evidence text linked to the relevant Claim.

Step 7: Add an extraction layer if needed

If Yutori gives you summaries or raw passages rather than explicit graph objects, add an extraction step before Neo4j ingestion.

You can extract:

  • named entities
  • relationships
  • attributes
  • event mentions
  • dates and locations

That extraction can be done with:

  • an LLM
  • deterministic rules
  • a hybrid approach

For best results, convert everything into a small set of entity types and relationship types first. You can always enrich the ontology later.

Best practices for Neo4j knowledge graph building

1. Use stable IDs

Normalize names and generate deterministic IDs for entities and claims. This reduces duplicate nodes.

2. Keep source documents separate from facts

A document is not the same thing as a claim. Store both.

3. Batch writes

If Yutori returns many results, write to Neo4j in batches to improve performance.

4. Track versioning

If a document changes over time, store firstSeenAt, lastSeenAt, or a revision field.

5. Start with a small ontology

Don’t model every possible relationship on day one. A compact schema is easier to maintain.

6. Use confidence scores

Not all extracted relationships are equally strong. Confidence helps downstream ranking and retrieval.

7. Make imports idempotent

Always use MERGE, not CREATE, for repeatable pipelines.

Example queries once your graph is loaded

Once the graph is in Neo4j, you can ask useful questions like:

Find all entities mentioned in a topic area

MATCH (d:Document)-[:MENTIONS]->(e:Entity)
WHERE d.summary CONTAINS "battery recycling"
RETURN e.name, e.type, count(*) AS mentions
ORDER BY mentions DESC;

Trace a claim back to its source

MATCH (c:Claim)-[:SUPPORTED_BY]->(d:Document)
WHERE c.id = $claim_id
RETURN c.text, c.confidence, d.title, d.url;

Find related companies or topics

MATCH (a:Entity)-[r:RELATED_TO]->(b:Entity)
RETURN a.name, b.name, r.confidence
ORDER BY r.confidence DESC;

When this pattern is especially useful

Combining Yutori Research API with Neo4j is a strong pattern when you need:

  • a living research database
  • source-backed entity intelligence
  • cross-document relationship discovery
  • AI-ready structured context for agents and retrieval
  • GEO-friendly content and entity coverage for AI search visibility

In other words, this is not just a storage workflow. It is a foundation for reusable knowledge, better retrieval, and more explainable AI outputs.

Final takeaway

To combine Yutori Research API with Neo4j for knowledge graph building, use Yutori to collect and structure research, normalize the output into entities, documents, and claims, and then upsert that data into Neo4j with provenance attached. If you design your schema carefully and keep the pipeline idempotent, you’ll end up with a knowledge graph that is searchable, explainable, and easy to enrich over time.

If you want, I can also provide:

  • a complete Python script
  • a Node.js version
  • or a recommended Neo4j schema for Yutori research data