Skip to main content

easeo

A deterministic SEO metadata library for content platforms.

Generate canonical URLs, titles, descriptions, robots directives, Open Graph, Twitter Cards, and JSON-LD from content entities. One Rust core with Python, Node.js, and Rust APIs.

PyPI npm crates.io Python Rust Tests Docs License

What is easeo

easeo is a deterministic SEO metadata library: you call it; it doesn't own your app's routing, rendering, or lifecycle. It takes a content entity, the route it lives at, and a site-wide configuration, and returns a complete SEO payload: canonical URL, title, description, robots directives, Open Graph, Twitter Cards, and JSON-LD.

All logic lives in the Rust core (easeo-core). The Python and Node.js packages wrap the same engine, so the same entity, route, and config produce identical output in every language. The cross-language conformance suite asserts that byte for byte.

The tutorial walks through installation and a first payload. Concepts explains the entity model, fallback chains, URL normalization, and validation rules.

Content Entity
      |
      v
   easeo
      |
      +-- canonical URL
      +-- title & description
      +-- robots directives
      +-- Open Graph
      +-- Twitter Cards
      +-- JSON-LD
      |
      v
 Deterministic Payload

Quick start

Every binding exposes the same call: pass an entity, a route, and a config, and get a payload back. The full tutorial covers configuration, overrides, rendering, and contracts.

Python

pip install easeo
from easeo import SEOConfig, SEOEntity, build_seo_payload

config = SEOConfig(
    canonical_host="example.com",
    public_base_url="https://example.com",
)

entity = SEOEntity(
    entity_type="post",
    title="Hello World",
    excerpt="An example post.",
)

payload = build_seo_payload(entity, "/blog/hello", config)
print(payload.render_html())

JavaScript / TypeScript

npm install @easeo/core
import { buildSeoPayload } from "@easeo/core";

const payload = buildSeoPayload(
  { entityType: "post", title: "Hello World", description: "An example post." },
  "/blog/hello",
  { canonicalHost: "example.com", publicBaseUrl: "https://example.com" }
);

console.log(payload.renderHtml());

Rust

cargo add easeo-core
use easeo_core::{SEOConfig, SEOEntity, EntityType, build_seo_payload};

let config = SEOConfig {
    canonical_host: "example.com".into(),
    public_base_url: "https://example.com".into(),
    ..Default::default()
};

let entity = SEOEntity {
    entity_type: EntityType::Post,
    title: Some("Hello World".into()),
    excerpt: Some("An example post.".into()),
    ..Default::default()
};

let payload = build_seo_payload(&entity, "/blog/hello", &config).unwrap();
assert_eq!(payload.canonical, "https://example.com/blog/hello");

Why easeo

Other SEO libraries do too much (scoring, keyword analysis, content rewriting), while easeo does one thing and does it well: generate deterministic SEO metadata from content entities.

Given the same inputs, easeo always produces the same output. No timestamps, no randomness, no environment reads, no hidden I/O. This makes SEO metadata something you can:

  • snapshot test: assert against expected payloads
  • hash: generate stable ETags
  • cache: safely memoize results
  • diff: detect changes across builds
  • validate in CI: commit your SEO intent as code with contracts

See Why easeo for the design goals and a comparison with other SEO tooling.

What easeo is not

  • Not a framework. Adapters are thin marshalling helpers, not lifecycle owners.
  • Not a scoring or keyword analysis tool.
  • Not a crawler or site auditor.
  • Not a content rewriter or generator.
  • Not a runtime service. It is a pure function over its inputs.
  • Not a full page renderer. It renders a head snippet, not a document.

Features

  • 11 entity types: home, post, page, video, taxonomy, search, product, organization, local_business, faq, other. See the entity model
  • URL normalization: HTTPS enforcement, lowercase paths, trailing slash control, duplicate slash collapse, tracking parameter removal. See URL normalization
  • JSON-LD schemas: WebSite, WebPage, Article, Product, Organization, LocalBusiness, FAQPage, BreadcrumbList, plus custom schemas via registry. See JSON-LD schemas and custom JSON-LD
  • Open Graph: full og:type, title, description, image, locale, audio, video
  • Twitter Cards: summary_large_image by default, with image alt, site, creator
  • Robots directives: index/noindex, follow/nofollow, max-snippet, max-image-preview, max-video-preview
  • SEO contracts: machine-readable SEO intent for CI validation. See the contract reference
  • Validation: built-in checks for title length, description, canonical format, OG image, robots directives. See validation
  • Hashing: SHA-256 payload hashing and ETag generation
  • HTML rendering: complete head snippet with meta tags, OG, Twitter, JSON-LD
  • Extension points: config-scoped hooks and schema registries. See hooks

Learn more in the guides and start from a recipe such as a blog post, product page, or search results page.

Packages

Package Registry Purpose
easeo PyPI Python bindings for the Rust core
easeo[fastapi], easeo[django], easeo[flask], easeo[zensical] PyPI extras Framework adapters and the docs extension
@easeo/core npm Node.js bindings for the Rust core
easeo-core crates.io Pure Rust engine with no I/O dependencies

Integrations

Per-framework setup guides live in the Integrations docs.

JavaScript / TypeScript

Framework Package API
Next.js @easeo/next easeoMetadata() returns a Next.js Metadata object
Astro @easeo/astro Build-time integration with contract emission
Vite @easeo/vite transformIndexHtml hook
Nuxt @easeo/nuxt useEaseoSeo() composable
SvelteKit @easeo/sveltekit buildEaseoPayload() and <EaseoHead />
React @easeo/react <EaseoHead /> component

Python

Framework Import
FastAPI from easeo.adapters.fastapi import EaseoSEO
Django from easeo.adapters.django import seo_head
Flask from easeo.adapters.flask import Easeo
Zensical from easeo.contrib.zensical import EaseoExtension

API

Symbol Description
build_seo_payload(entity, route_path, config, overrides=None) Builds the deterministic SEOPayload
SEOConfig Site-wide configuration: host, base URL, defaults, hooks, registries
SEOEntity Content entity: type, title, excerpt, images, dates, breadcrumbs, FAQ, product fields
SEOOverrides Highest-precedence per-call overrides
SEOPayload Output with render_html(), to_dict(), to_json(), hash(), and etag()
URLPolicy Canonical URL normalization policy
build_seo_contract Builds a machine-readable SEO contract
validate_payload Runs the built-in validation checks
HookRegistry, SchemaRegistry Config-scoped post-processing and JSON-LD generators
SEOEntityBuilder, from_blog_post, from_product, from_faq Ergonomic entity constructors

Names are shown in Python. JavaScript uses camelCase and Rust uses snake_case. Full signatures live in the Python, JavaScript, and Rust API references.

Documentation

  • Tutorial: installation, first payload, rendering, contracts, configuration
  • Concepts: determinism, entity and payload models, fallback chains, URL normalization, schemas, validation
  • Guides: custom JSON-LD, hooks, contracts in CI, framework recipes
  • Reference: Python, JavaScript, and Rust APIs, contracts, errors
  • Integrations: per-framework setup
  • Recipes: blog post, product page, category page, search results
  • Examples: core and framework examples for every binding

LLM-friendly versions: llms.txt and llms-full.txt.

Building

# Rust core
cargo build --workspace --release

# Python bindings
pip install maturin
maturin develop -m crates/easeo-python/Cargo.toml

# Node bindings
npm install -g @napi-rs/cli@^3
cd packages/core
napi build --platform --release --manifest-path ../../crates/easeo-node/Cargo.toml
cp ../../crates/easeo-node/*.node .

Testing

# Rust
cargo test --workspace

# Python
pytest tests/python/

# JavaScript
node --test tests/javascript/*.cjs

# Cross-language conformance
python tests/conformance/test_conformance.py

Next steps

Contributors

License

MIT

Metadata

Release files for easeo 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for easeo 0.2.0
File
easeo-0.2.0-cp310-abi3-win_amd64.whl CPython 3.10 abi3 Windows x86-64 Details
easeo-0.2.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.10 abi3 Linux glibc 2.17+ x86-64 Details
easeo-0.2.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.10 abi3 Linux glibc 2.17+ ARM64 Details
easeo-0.2.0-cp310-abi3-macosx_11_0_arm64.whl CPython 3.10 abi3 macOS 11.0+ ARM64 Details
easeo-0.2.0-cp310-abi3-macosx_10_12_x86_64.whl CPython 3.10 abi3 macOS 10.12+ x86-64 Details

Total release size: 3.7 MB

Release files / easeo-0.2.0-cp310-abi3-win_amd64.whl

Download URL easeo-0.2.0-cp310-abi3-win_amd64.whl
Size 625.5 kB
Tags CPython 3.10 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
ff7fdf08252c0a22637600b53f1cebd607198e424a4a69a5060ed70a23494699
BLAKE2b-256 checksum
How to use checksums
b2b572a8469f3fb1d3d106c02c1da9221a2c2fdfcd683f245a2890f192b82a05
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.

Transparency log

Release files / easeo-0.2.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL easeo-0.2.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 801.5 kB
Tags CPython 3.10 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
9e974fb0abb6d92cabc3cfe8b3f05b083d1abdb6feb0ca55eba2a6cde2aa9b69
BLAKE2b-256 checksum
How to use checksums
c3eb507c8d30a9b2f196cb07c7afde85863eb3159b793a6383001a37a25f6dbd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.

Transparency log

Release files / easeo-0.2.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL easeo-0.2.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 787.4 kB
Tags CPython 3.10 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
879d82c77024b9c137327307dec9be80269413ea58e692c21844c83d59bd32ea
BLAKE2b-256 checksum
How to use checksums
842bd2c81f3b9d5aa496660a8913e3e2851d4c5775b977e8461396d5ba64fe18
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.

Transparency log

Release files / easeo-0.2.0-cp310-abi3-macosx_11_0_arm64.whl

Download URL easeo-0.2.0-cp310-abi3-macosx_11_0_arm64.whl
Size 726.0 kB
Tags CPython 3.10 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
8d6a485a7c10a88064ea52b550e7d3e52374fc2aa4a564c924e541a598a9feea
BLAKE2b-256 checksum
How to use checksums
f40bec75bb6a8a38fd3d86ef3205daf1bd73e9aa0230613c74020b416751df32
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.

Transparency log

Release files / easeo-0.2.0-cp310-abi3-macosx_10_12_x86_64.whl

Download URL easeo-0.2.0-cp310-abi3-macosx_10_12_x86_64.whl
Size 737.2 kB
Tags CPython 3.10 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
544e6a985237db0d20c3ba78a00f2df23ebfad8052604ef99006b7af0da6f5fe
BLAKE2b-256 checksum
How to use checksums
b40416eda6c6cb77a730c76218275332ba29fd30e423f99180c68aebe17cc19a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0 This release

5 release files

0.1.1

5 release files

0.1.0

5 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page