Beta — API stabilizing toward 1.0

All-in-One Data Pipeline Engine

A Python framework for data pipelines — batch or streaming, scheduled or event-driven — with a fast query engine, data-quality checks, and an operator Web UI built in. One pip install, no cluster to run.

The fastest engine we've measured at every scale — and a single node outruns 4-node clusters. Zero tuning, zero flags.

9.6× faster than a 4-node cluster — on a single node
38× the price-performance of the clusters: ¼ the hardware, 9.6× the speed
22/22 TPC-H queries pass, value-validated, on shipped defaults — zero flags

Every engine we measured — TPC-H SF=100, same hardware class

Sum of 22 query medians · single nodes are 1× c7i.4xlarge, clusters are 4× c7i.4xlarge

ematix-flow · 1 node 51.8 s
DuckDB · 1 node 59.0 s
ematix mesh · 4 nodes 61.9 s
Polars · 1 node · 16/22 251 s*
PySpark · 4 nodes 375 s
Trino · 4 nodes 497 s

*completed 16/22 queries — the total covers only those 16 (the missing queries would only add time). Five exceed the 32 GB box with Polars's in-memory engine; one lacks a Polars SQL variant. PySpark completes all 22 after we gave it EBS shuffle scratch. The mesh total predates the Jul 10 planner fix; a refresh is in progress.

Faster

Distributed, TPC-H SF=100

6–8× faster than the cluster engines — identical 4 nodes.

ematix-flow (auto mesh) 61.9 s
Trino 497 s
PySpark 375 s

4× c7i.4xlarge · SF=10: 5.6× vs Trino, 6.5× vs PySpark · SF=1: 7.2× / 19×

Cheaper

Every engine, SF=10

One ematix node beats the single-node engines and the 4-node clusters.

ematix-flow · 1 node 4.8 s
DuckDB · 1 node 5.8 s
Polars · 1 node · 19/22 9.1 s
Trino · 4 nodes 56.4 s
PySpark · 4 nodes 65.0 s

Same queries, same S3 parquet — one node outruns the 4-node clusters ~12–14×

Simpler

Single node, SF=1, zero tuning

Fastest single-node engine at every scale we measured.

ematix-flow 0.7 s
DuckDB 1.4 s
Polars · 21/22 2.7 s

Same lead holds up-scale: SF=10 4.8 s vs 5.8 s · SF=100 51.8 s vs 59.0 s

Measured on AWS, not a laptop — TPC-H on c7i.4xlarge (16 vCPU / 32 GB), July 2026, shipped pip install ematix-flow defaults, no flags, results value-validated. Full benchmarks →

Documentation

Start the tutorial →

Featured concepts

All concepts →

Quick peek

Full tutorial →

A workflow with a composite trigger (event + cron) plus within-DAG ordering.

from ematix_flow import ematix, ManagedTable, Annotated, BigInt, Text, pk

@ematix.connection
class warehouse:
    kind = "postgres"
    url = "${WAREHOUSE_URL}"

class OrdersExtracted(ManagedTable):
    __schema__ = "analytics"; __tablename__ = "orders_extracted"
    order_id: Annotated[BigInt, pk()]
    customer_id: BigInt
    amount_cents: BigInt

class OrdersEnriched(ManagedTable):
    __schema__ = "analytics"; __tablename__ = "orders_enriched"
    order_id: Annotated[BigInt, pk()]
    amount_bucket: Text

@ematix.job(name="extract_orders",
            target=OrdersExtracted, target_connection="warehouse",
            mode="merge", keys=("order_id",))
def extract_orders(conn):
    return "SELECT order_id, customer_id, amount_cents FROM raw.orders"

@ematix.job(name="enrich_orders",
            target=OrdersEnriched, target_connection="warehouse",
            mode="merge", keys=("order_id",),
            depends_on=["extract_orders"])
def enrich_orders(conn):
    return "SELECT order_id, CASE WHEN amount_cents < 10000 THEN 'small' ELSE 'large' END AS amount_bucket FROM analytics.orders_extracted"

# Workflow declares the trigger; member jobs declare their DAG position.
ematix.workflow(
    name="orders_etl",
    triggered_by=["upstream_workflow"],
    schedule="0 21 * * *",
    timezone="America/New_York",
    jobs=["extract_orders", "enrich_orders"],
)
Currently in beta

On PyPI as ematix-flow. The core API is settling toward 1.0; pinning a minor version is still recommended. Bug reports and design pushback during the beta window are exactly what we want — file issues on GitHub.