# Raphtory: a temporal graph database written in Rust with Python APIs

> Raphtory is an in-memory vectorised graph database written in Rust with Python bindings on top, built around time-stamped edges, rolling windows and time-travel queries, and it can run embedded or as a GraphQL server. It suits analysts who need temporal graph analytics over large event streams, and the on-disk scaling that relieves the memory ceiling sits behind a subscription.

**Pometry/Raphtory** — Scalable graph analytics database powered by a multithreaded, vectorized temporal engine, written in Rust

- Repository: https://github.com/Pometry/Raphtory
- Website: https://raphtory.com
- Stars: 649 · Forks: 71
- Language: Rust
- License: GPL-3.0
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/pometry-raphtory

## What Raphtory is: a temporal graph engine, not a general graph database

Raphtory is described as an in-memory vectorised graph database written in Rust with friendly Python APIs on top. The README makes two performance claims that frame the design: it is fast, and it scales to hundreds of millions of edges on a laptop, and it can be dropped into an existing pipeline with a simple pip install raphtory.

The word that matters in the repository description is temporal. This is a graph database where time is a first-class part of the data model rather than a column you filter on. Every node and every edge is added with a timestamp, every property has a history, and the graph can be viewed as it was at a given moment.

The feature list in the README is broad: it can run embedded or as a server instance using GraphQL, and it supports time traveling, full-text search, multilayer modelling, and advanced analytics beyond simple querying, with automatic risk detection, dynamic scoring and temporal motifs named as examples.

The multilayer point is worth a sentence, because it is a modelling decision rather than a feature checkbox. Networks of people, accounts, devices and transactions are rarely one graph, and modelling them as layers lets algorithms run across them.

The project is developed by Pometry, the licence is GPL-3.0, and the default branch is db_v4, which names the fourth generation of the storage layer rather than a release line. The published releases are v0.18.5 and v0.18.0, both dated 2026-06-23, and v0.17.0 from 2026-03-09, with the last push to the repository on 2026-10-20 and the repository not archived.

## Installing: pip for Python, cargo for Rust, and a version caveat

Raphtory is available for Python and Rust, and both installs are one command.

For Python you must be using version 3.8 or higher:

```bash
pip install raphtory
```

For Rust, Raphtory is hosted on crates and can be included in your project with cargo add:

```bash
cargo add raphtory
```

The README states Rust version 1.77 or higher for the crates package, and the workspace manifest pins a stricter requirement. The Cargo.toml sets rust-version = 1.95.0 in the workspace package section, and the Dockerfile builds with an ARG RUST_VERSION defaulting to 1.89.0. Those three numbers do not agree, and the practical rule is to follow the manifest rather than the README, since a manifest rust-version is what cargo enforces when resolving the dependency.

The Rust build is not a plain cargo build. The workspace declares eighteen members, with raphtory as the default member, and the Python bindings are built with maturin rather than setuptools: the Makefile's build-all target runs maturin develop inside the python directory, and the wheel targets are maturin build -r, with a minimal-wheel variant built with --no-default-features. That last target matters for packaging, because a slim build without the default feature set is what you would want in a container image.

For containers, the Dockerfile is a standard cargo-chef multistage build. It installs cargo-chef, generates a recipe, cooks the dependencies in a separate layer, builds the raphtory-graphql binary, and copies it into a debian:bookworm-slim runtime image with a volume mounted at /var/lib/raphtory for storage.

## The Python API: nodes and edges with timestamps and properties

The README's worked example is the fastest way to see the model, and it starts with a graph, three nodes and three edges:

```python
from raphtory import Graph
from raphtory import algorithms as algo
import pandas as pd

# Create a new graph
graph = Graph()

# Add some data to your graph
graph.add_node(timestamp=1, id="Alice")
graph.add_node(timestamp=1, id="Bob")
graph.add_node(timestamp=1, id="Charlie")
graph.add_edge(timestamp=2, src="Bob", dst="Charlie", properties={"weight": 5.0})
graph.add_edge(timestamp=3, src="Alice", dst="Bob", properties={"weight": 10.0})
graph.add_edge(timestamp=3, src="Bob", dst="Charlie", properties={"weight": -15.0})
```

Three things are visible in those lines. The timestamp is a required argument on both add_node and add_edge, which is the enforcement of the temporal model: an event without a time cannot be recorded. The edge carries a properties dictionary, so an edge is a bundle of named values rather than a bare link. And the same edge, Bob to Charlie, is added twice at two different timestamps with two different weights, which is the case the rest of the example then interrogates.

The graph object reports itself directly. Printing it yields Graph(number_of_edges=2, number_of_nodes=3, earliest_time=1, latest_time=3), a one-line summary with the counts and the temporal extent of the data, which is a fast sanity check that an ingestion did what you expected.

The example then iterates graph.rolling(window=1) and, for each window, collects graph_view.earliest_time with each node's name and its out_degree() and in_degree(). Degrees are functions rather than fields, which fits the model, because a degree at a point in time differs from the degree now.

## Time travel: reading an edge at a moment and reading its history

The second half of the example is the part that distinguishes a temporal database from a graph database with a time column.

```python
cb_edge = graph.edge("Bob", "Charlie")
weight_history = cb_edge.properties.temporal.get("weight").items()
print(
    "The edge between Bob and Charlie has the following weight history:", weight_history
)

# Compare this weight between time 2 and time 3
weight_change = cb_edge.at(2)["weight"] - cb_edge.at(3)["weight"]
```

Two distinct access patterns appear. The first asks an edge for the full history of one property, and properties.temporal.get("weight").items() returns that history as a list of time and value pairs. For the example edge the output is [(2, 5.0), (3, -15.0)], which is the two additions recorded with their timestamps and weights.

The second asks for the value at a specific time, and edge.at(2) returns the state of the edge as of time 2, so the subtraction of at(2) and at(3) yields the 20.0 point change the example prints. This is the query a fraud or drift investigation wants: not what is the edge now, but what did it look like when the decision was made.

The same pattern applies to the whole graph rather than to one edge, since the rolling window in the earlier part of the example yields graph views, each of which carries its own earliest_time. Nothing in the API requires you to copy the graph per snapshot, because the history is stored on the object.

## Analytics and the GraphQL server

Algorithms are reached through a separate module, from raphtory import algorithms as algo, and the example ends with a standard graph algorithm applied to the whole history:

```python
top_node = algo.pagerank(graph).top_k(5).max_item()
print(
    "The most important node in the graph is",
    top_node[0].name,
    "with a score of",
    top_node[1],
)
```

The chain is worth noticing. The algorithm returns a result object, that object has a top_k method taking a count, and that returns an object with max_item, so ranking is a lazy object pipeline rather than a list you materialise. The README's own output for the example graph shows the top node as Charlie with a score of 0.4744116163405977.

The README also names the analytics that go beyond classic graph algorithms: automatic risk detection, dynamic scoring and temporal motifs. Those are the features that come with a temporal model rather than from it, and they are the reason to choose a temporal engine over a general-purpose graph database for fraud, intrusion or churn work.

The server mode exposes the same data over GraphQL, and the Python API can host it in-process:

```python
from raphtory import Graph
from raphtory.graphql import GraphServer
import pandas as pd
import os

# URL for lord of the rings data from our main tutorial
url = "https://raw.githubusercontent.com/Raphtory/Data/main/lotr-with-header.csv"
df = pd.read_csv(url)
```

The point of the GraphQL layer is integration. The README describes it as making it easy to integrate graphy analytics with web applications, so a dashboard or an existing React front end can query the graph without a bespoke API layer, and the same schema can be linted in CI, which the Makefile does with a GraphQL schema linter that checks that fields and types have descriptions.

The workspace also contains raphtory-cypher and raphtory-server crates, so the query surface is wider than the README's example shows.

## Inside the workspace: eighteen crates, maturin and nextest

The repository layout describes the architecture. The Rust workspace members list names raphtory as the core library, raphtory-core and raphtory-api for the internal APIs, raphtory-api-macros for the macro layer, raphtory-storage for persistence, raphtory-graphql and raphtory-server for serving, raphtory-cypher for query support, raphtory-vectors for vectorised execution, raphtory-itertools, raphtory-benchmark, raphtory-tests and raphtory-graphql-tests for testing, plus db4-graph and db4-storage, which are the v4 storage components that the db_v4 default branch is named after.

The toolchain choices show how seriously performance is taken. The dev profile sets split-debuginfo to unpacked with debug 1 and opt-level 1, and the comment above it says that debug symbols are using a lot of resources, which is a specific kind of attention to build times. There is a release-with-debug profile for profiling, a with-debug profile, and a build-fast profile for one-time builds such as docs and CI.

Tests run with cargo nextest run --all, and the check target uses cargo hack to compile the workspace across every feature combination, skipping the extension-module, default, vectors and datafusion features. That last skip list tells you the feature matrix is large enough that some combinations are not checked, which is worth knowing if you enable an unusual feature.

The Python side is built with maturin, the stubs are generated into a stub_gen directory, formatting uses cargo plus nightly fmt for Rust, and the Makefile's check-pr target chains tidy and test-all together, so a pull request runs formatting, stub generation and both test suites.

The examples directory is broad: python, rust, netflow for network flow data, grafana, docker and custom-gql-apis, which is the practical evidence for the README's claim that it can be dropped into an existing pipeline.

## The memory ceiling, the subscription and the GPL licence

Two constraints shape the adoption decision, and both are stated in the README rather than buried.

The first is memory. Raphtory's headline mode is in-memory, which is where the speed comes from and why the README can claim hundreds of millions of edges on a laptop. The escape hatch is described in one sentence: with the subscription model, Raphtory also supports out-of-memory, meaning on-disk, scaling with no performance loss. The claim about no performance loss is the vendor's, and the practical consequence is that the escape hatch is not a free tier you can rely on without checking what the subscription actually gives you.

The second is the licence. GPL-3.0 is a copyleft licence, and the README and the workspace metadata both name it. How its obligations apply to a particular deployment, for instance running the engine as a service rather than embedding it in a product you distribute, is a question for whoever owns the compliance decision, and this is not legal advice. The repository ships a LICENSE file and a SECURITY.md for the responsible disclosure process.

There are other limits worth naming. The version numbers are pre-1.0, so the API is still moving, and the fact that the default branch is db_v4 rather than main says the storage layer is mid-generation, with the db4-graph and db4-storage crates in the tree and the workspace version at 0.18.0.

The realistic alternative for someone with a temporal graph problem is to build the same queries on a general-purpose database and keep the temporal logic in application code, which is what many teams end up doing. The difference in approach is that Raphtory stores the history inside the engine and lets you query it directly, with time travel and rolling windows as operations rather than as SQL you write yourself, at the cost of a dependency with its own licence and its own engine to operate.

## Conclusion

Raphtory earns its place when your data is an event stream over a graph and your questions are about time rather than about topology alone, since time travel, rolling windows and temporal property histories are the core of the API rather than an add-on. It does not fit a team that needs a permissively licensed dependency in a commercial product, since the licence is GPL-3.0, and the on-disk scaling path that removes the memory ceiling is tied to a subscription. Verify first that your data can be expressed as timestamped nodes and edges, that the Rust toolchain you have matches the version the workspace requires, and that the memory footprint of the in-memory mode fits the machine you plan to run it on.

## FAQ

### How do I install Raphtory?

For Python you need version 3.8 or higher and can run pip install raphtory. For Rust the crate is published on crates.io and can be added with cargo add raphtory. The README states Rust 1.77 or higher, while the workspace manifest sets rust-version 1.95.0, so the manifest is the constraint cargo will enforce.

### Can Raphtory run as a server instead of being embedded?

Yes. The README says it can run embedded or as a server instance using GraphQL, and the Python API exposes a GraphServer class from raphtory.graphql. The GraphQL schema is generated from the binary and linted in CI with a schema linter that checks fields and types have descriptions.

### How do you query a graph at a specific point in time in Raphtory?

Edges expose an at method for a given time, so edge.at(2) returns the edge state at time 2, and properties.temporal.get("weight").items() returns the full history of a property as time and value pairs. The graph itself can be iterated as rolling windows with graph.rolling(window=1).

### Does Raphtory support datasets larger than memory?

The README states that with the subscription model, Raphtory supports out-of-memory, on-disk scaling with no performance loss. Without that subscription, the in-memory model is the default and the README's scale claim is hundreds of millions of edges on a laptop.

### What licence does Raphtory use and can I use it commercially?

The workspace package metadata sets license = "GPL-3.0", and the repository ships a LICENSE file. Whether those terms fit your deployment depends on how you distribute the software, so check them against your own situation.

### Which analytics does Raphtory provide beyond graph queries?

The README names automatic risk detection, dynamic scoring and temporal motifs as examples of analytics beyond simple querying, alongside the standard algorithms module exposed in Python as algo, which the example uses to run pagerank and take a top_k result.

## Sources

- [License: GPL-3.0](https://github.com/Pometry/Raphtory/blob/db_v4/LICENSE)
- [Pometry/Raphtory on GitHub](https://github.com/Pometry/Raphtory)
- [Project website](https://raphtory.com)
- [README](https://github.com/Pometry/Raphtory/blob/db_v4/README.md)
- [Releases](https://github.com/Pometry/Raphtory/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/pometry-raphtory
