Framework-agnostic canonical URL normalization and SEO payload generation
Project description
seoslug
Canonical URL normalization + deterministic SEO payload generation for content platforms.
What is seoslug?
seoslug turns your content entities into production-ready SEO metadata, canonical URLs, Open Graph, Twitter Cards, and JSON-LD, deterministically. Same input always produces the same output, making your SEO layer testable, cacheable, and predictable.
One function call. Complete SEO coverage. No surprises.
Quick start
from seoslug import SEOConfig, URLPolicy, SEOEntity, build_seo_payload
config = SEOConfig(
canonical_host="blog.example.com",
public_base_url="https://blog.example.com",
url_policy=URLPolicy(
enforce_https=True,
lowercase_paths=True,
trailing_slash="never",
),
)
entity = SEOEntity(
entity_type="post",
slug="my-post",
title="My Post",
excerpt="Example excerpt",
)
payload = build_seo_payload(entity, "/posts/my-post", config)
That's it. payload contains everything you need: title, description, canonical, og, twitter, and schema_jsonld, ready to inject into your HTML.
Why deterministic?
Most SEO tools produce different output for the same input: random cache busters, timestamps, or dictionary key order changes. seoslug does none of that.
payload1 = build_seo_payload(entity, path, config)
payload2 = build_seo_payload(entity, path, config)
assert payload1 == payload2 # Always True
This seemingly small property unlocks powerful workflows. You can commit expected SEO output to Git and validate it in CI: if SEO changes, your build fails. The same URL always generates an identical payload, so you can cache forever without invalidation logic. Diffing staging against production instantly reveals configuration drift, and you can track how your SEO evolves right alongside your code.
What seoslug handles
URL normalization: HTTPS enforcement, trailing slash policy, lowercase paths, query parameter sorting. Optionally strips tracking parameters using detrack.
Open Graph and Twitter Cards: og:title, og:image, twitter:card, and everything else search engines and social platforms expect.
JSON-LD: Auto-generates schema.org schemas (Article, WebPage, VideoObject, CollectionPage, SearchResultsPage) based on your entity type. Map any entity type to any schema type via config. Override with custom JSON-LD when needed.
Robots directives: index/noindex and follow/nofollow based on entity status.
Configurable fallbacks: Define what happens when a field is missing. Title can fall back to meta_title, then entity.title, then "Untitled". Description falls back from meta_description to entity.excerpt to an auto-generated HTML body snippet.
And everything is pure: no environment variables, no system clock, no random numbers, no external API calls.
Documentation
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file seoslug-1.0.2.tar.gz.
File metadata
- Download URL: seoslug-1.0.2.tar.gz
- Upload date:
- Size: 15.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c3736cbde865bc3a2495bf60480e4f9ffd702f4d4cae133ac024165179d02c06
|
|
| MD5 |
1e3e9eccc1d949f3a380704bb841cbb1
|
|
| BLAKE2b-256 |
a77cff74a548e26624ee0e4ea87ef984a51b6e94a186144dc22ce4b56a3dc4b2
|
File details
Details for the file seoslug-1.0.2-py3-none-any.whl.
File metadata
- Download URL: seoslug-1.0.2-py3-none-any.whl
- Upload date:
- Size: 10.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
24d1afd49ec072061eb462618680991feb5ec8d2391a5776b374f27d00edea1d
|
|
| MD5 |
308654510a71f4ea10a04b839a91576a
|
|
| BLAKE2b-256 |
1f1ee2279fd07f41e1e85121ce8276b60861479e186e77ac0e788bbd4e02ab5e
|