Python package that generates SEO-friendly XML sitemaps
Project description
Sitemapy
Sitemapy is a Python package for generating SEO-friendly XML sitemaps.
Features
- 🚀 Simple and intuitive API for sitemap generation
- 📝 Support for all standard sitemap attributes (priority, changefreq, lastmod)
- 🌍 Full hreflang support for multilingual sites
- 📦 Sitemap index support for large sites
- 🗜️ Built-in compression (gzip) support
- 🔒 Safe XML parsing with defusedxml
- ⚡ Method chaining for cleaner code
- 🔄 Load and modify existing sitemaps
Table of Contents
- Installation
- Quick Start
- Usage Examples
- Sitemap Extensions
- Real-World Examples
- API Reference
- Best Practices
- Requirements
- Contributing
- License
Installation
pip install sitemapy
Quick Start
from sitemapy import Sitemap
sitemap = Sitemap.from_list([
"https://example.com/",
"https://example.com/about/",
])
sitemap.write_to_file("sitemap.xml")
Output (sitemap.xml):
<?xml version='1.0' encoding='utf-8'?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://example.com/</loc>
</url>
<url>
<loc>https://example.com/about/</loc>
</url>
</urlset>
Usage Examples
Adding URLs Incrementally
from sitemapy import Sitemap
sitemap = Sitemap()
sitemap.add_url("https://example.com/", lastmod="2025-01-15")
sitemap.add_url("https://example.com/blog/", priority=0.8)
sitemap.write_to_file()
Setting Dates
from sitemapy import Sitemap
sitemap = Sitemap.from_list([
"https://example.com/",
"https://example.com/about/"
])
# Set all URLs to a specific date
sitemap.set_all_lastmod("2025-12-01")
# Or set all URLs to today's date
sitemap.set_all_lastmod_to_today().write_to_file("today-sitemap.xml")
Using URLEntry Objects for More Control
from sitemapy import Sitemap, URLEntry
url = URLEntry(
loc="https://example.com/important-page/",
lastmod="2025-10-21",
changefreq="weekly",
priority=0.9
)
sitemap = Sitemap()
sitemap.add_url(url)
sitemap.write_to_file()
Working with Existing Sitemaps
from sitemapy import Sitemap
# Load, modify, and save
sitemap = Sitemap.from_file("existing-sitemap.xml")
sitemap.add_url("https://example.com/new-page/")
sitemap.deduplicate()
sitemap.write_to_file("updated-sitemap.xml")
Hreflang Support
Sitemapy makes creating hreflang alternates for multilingual sites easy:
from sitemapy import Sitemap, URLEntry, HreflangAlternate
# Create a URL with language alternates
url = URLEntry(loc="https://www.example.com/")
# Add alternates individually
url.add_alternate(href="https://www.example.de/", hreflang="de-de")
# Or using HreflangAlternate objects
href_alt = HreflangAlternate(href="https://www.example.es/", hreflang="es-es")
url.add_alternate(href_alt)
# Or add multiple alternates at once
url.add_alternates([
{"href": "https://www.example.de/", "hreflang": "de-de"},
{"href": "https://www.example.es/", "hreflang": "es-es"},
{"href": "https://www.example.fr/", "hreflang": "fr-fr"},
])
# Add to sitemap
sitemap = Sitemap()
sitemap.add_url(url)
sitemap.write_to_file()
Sitemap Index
For large sites with multiple sitemaps, use a sitemap index:
from sitemapy import SitemapIndex
# Create an index of multiple sitemaps
index = SitemapIndex.from_list([
"https://example.com/sitemap-posts.xml",
"https://example.com/sitemap-pages.xml",
"https://example.com/sitemap-products.xml",
])
# Add more sitemaps with metadata
index.add_sitemap(
"https://example.com/sitemap-images.xml",
lastmod="2025-10-27"
)
index.write_to_file("sitemap-index.xml")
Compression
Generate compressed sitemaps for better performance and reduced bandwidth:
from sitemapy import Sitemap
sitemap = Sitemap.from_list([
"https://example.com/",
"https://example.com/about/",
])
# Creates sitemap.xml.gz
sitemap.write_compressed("sitemap.xml.gz")
# Or let it use the default name
sitemap.write_compressed() # Creates sitemap.xml.gz
Sitemap Extensions
Google recognizes a set of extensions for sitemaps. Sitemapy can intake and create News and Image elements (Video coming soon...)
URLEntry can contain an arrays of ImageEntry. URLEntry can contain a single NewsEntry.
Images
from sitemapy import URLEntry, ImageEntry
url = URLEntry(loc="https://www.example.com/")
# Create image(s) with just a URL string
url.add_image(image="https://www.example.com/cat.png/")
url.add_image(image="https://www.example.com/dog.png/")
# Create image(s) from an ImageEntry
image_entry_one = ImageEntry(image="https://www.example.com/cat.png/")
image_entry_two = ImageEntry(image="https://www.example.com/dog.png/")
url.add_image(image_entry_one)
url.add_image(image_entry_two)
News
News elements can only be added by building a NewsEntry object.
from sitemapy import URLEntry, NewsEntry
url = URLEntry(loc="https://www.example.com/")
news_entry = NewsEntry(
publication_name="The New York Times",
publication_language="en",
publication_date="2025-12-01",
title="First Contact Made"
)
url.add_news_entry(news_entry)
Real-World Examples
Generate Sitemap from Database
from sitemapy import Sitemap, URLEntry
from datetime import datetime
sitemap = Sitemap()
# Example: Blog posts from database
for post in get_blog_posts(): # Your database query
sitemap.add_url(
URLEntry(
loc=f"https://example.com/blog/{post.slug}/",
lastmod=post.updated_at.strftime("%Y-%m-%d"),
changefreq="monthly",
priority=0.8
)
)
sitemap.deduplicate().write_to_file("blog-sitemap.xml")
Generate from Multiple Sources
from sitemapy import Sitemap, SitemapIndex
# Create separate sitemaps for different content types
posts_sitemap = Sitemap()
for post in get_posts():
posts_sitemap.add_url(f"https://example.com/posts/{post.id}/")
posts_sitemap.write_compressed("sitemap-posts.xml.gz")
pages_sitemap = Sitemap()
for page in get_pages():
pages_sitemap.add_url(f"https://example.com/{page.slug}/", priority=0.9)
pages_sitemap.write_compressed("sitemap-pages.xml.gz")
# Create an index
index = SitemapIndex.from_list([
"https://example.com/sitemap-posts.xml.gz",
"https://example.com/sitemap-pages.xml.gz",
])
index.write_to_file("sitemap-index.xml")
Filter and Update URLs
from sitemapy import Sitemap
# Load existing sitemap
sitemap = Sitemap.from_file("sitemap.xml")
# Find all blog URLs
blog_urls = sitemap.get_urls_by_pattern(r"/blog/")
# Update their lastmod date
for url in blog_urls:
url.lastmod = "2025-10-27"
url.changefreq = "weekly"
sitemap.write_to_file("updated-sitemap.xml")
Create Multilingual Sitemap
from sitemapy import Sitemap, URLEntry
sitemap = Sitemap()
# Add homepage with language alternates
homepage = URLEntry(loc="https://example.com/en/")
homepage.add_alternates([
{"href": "https://example.com/en/", "hreflang": "en"},
{"href": "https://example.com/de/", "hreflang": "de"},
{"href": "https://example.com/es/", "hreflang": "es"},
{"href": "https://example.com/fr/", "hreflang": "fr"},
])
sitemap.add_url(homepage)
# Add more pages...
for page in get_pages():
url = URLEntry(loc=f"https://example.com/en/{page.slug}/")
url.add_alternates([
{"href": f"https://example.com/en/{page.slug}/", "hreflang": "en"},
{"href": f"https://example.com/de/{page.slug}/", "hreflang": "de"},
{"href": f"https://example.com/es/{page.slug}/", "hreflang": "es"},
{"href": f"https://example.com/fr/{page.slug}/", "hreflang": "fr"},
])
sitemap.add_url(url)
sitemap.write_to_file("multilingual-sitemap.xml")
API Reference
Sitemap
Main class for creating and managing sitemaps.
Class Methods:
from_list(urls)- Create sitemap from list of URL strings or URLEntry objectsfrom_file(path)- Load existing sitemap from XML file
Instance Methods:
add_url(url, **kwargs)- Add single URL (string or URLEntry)remove_url(url)- Remove URL by location stringget_urls_by_pattern(pattern)- Filter URLs by regex patterndeduplicate()- Remove duplicate URLsset_all_lastmod(date)- Set lastmod for all URLs to specified dateset_all_lastmod_to_today()- Set lastmod for all URLs to today's datewrite_to_file(filename)- Save as uncompressed XML (default: "sitemap.xml")write_compressed(filename)- Save as compressed .xml.gz (default: "sitemap.xml.gz")
Special Methods:
__len__()- Returns number of URLs in sitemap__iter__()- Allows iteration over URLEntry objects
URLEntry
Represents a single URL in a sitemap with optional metadata.
Constructor:
URLEntry(
loc: str, # Required: URL location
lastmod: str = None, # Last modification date (YYYY-MM-DD)
changefreq: str = None, # Change frequency (always, hourly, daily, weekly, monthly, yearly, never)
priority: float = None # Priority 0.0-1.0
)
Methods:
add_alternate(href_alt=None, hreflang="", href="")- Add single hreflang alternateadd_alternates(alternates)- Add multiple hreflang alternates from list of dicts
HreflangAlternate
Represents a language/region alternate for a URL.
Constructor:
HreflangAlternate(
hreflang: str, # Language code (e.g., "en-us", "de-de")
href: str # Alternate URL
)
SitemapIndex
Class for creating and managing sitemap index files.
Class Methods:
from_list(urls)- Create index from list of sitemap URLs or IndexEntry objectsfrom_file(path)- Load existing sitemap index from XML file
Instance Methods:
add_sitemap(url, **kwargs)- Add sitemap URL (string or IndexEntry)remove_sitemap(url)- Remove sitemap by location stringwrite_to_file(filename)- Save as XML (default: "sitemap-index.xml")
Special Methods:
__len__()- Returns number of sitemaps in index__iter__()- Allows iteration over IndexEntry objects
IndexEntry
Represents a single sitemap reference in a sitemap index.
Constructor:
IndexEntry(
loc: str, # Required: Sitemap URL location
lastmod: str = None # Last modification date (YYYY-MM-DD)
)
Best Practices
Sitemap Limits
- Maximum 50,000 URLs per sitemap file
- Maximum 50MB uncompressed size per sitemap
- Use a sitemap index for sites with more than 50,000 URLs
- Consider splitting large sitemaps by content type or date
URL Guidelines
- Use absolute URLs (include protocol and domain)
- URLs should be canonical (no duplicates or redirects)
- Update
lastmodwhen content actually changes - Use
priority(0.0-1.0) to indicate relative importance within your site- Note: Priority is relative to other URLs on your site, not the entire web
Change Frequency
Valid changefreq values:
always- Changes with every accesshourly- Changes every hourdaily- Changes dailyweekly- Changes weeklymonthly- Changes monthlyyearly- Changes yearlynever- Archived URLs that never change
Performance Tips
- Use compression (
write_compressed()) for large sitemaps - Generate sitemaps incrementally during off-peak hours
- Submit sitemap location to search engines via robots.txt:
Sitemap: https://example.com/sitemap.xml
Hreflang Best Practices
- Each language version should reference all other versions (including itself)
- Use correct language codes (ISO 639-1) and optional region codes (ISO 3166-1 Alpha 2)
- Ensure all alternate URLs are accessible and return 200 status codes
- Common format:
en-US,de-DE,es-ES,fr-FR - See Google's documentation here.
Requirements
- Python 3.10+
- defusedxml 0.7.0
Contributing
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
Security
Sitemapy uses defusedxml instead of Python's standard XML library when parsing external sitemap files. This protects against XML vulnerabilities like billion laughs attacks and XML external entity (XXE) attacks that could occur when loading untrusted sitemap files.
Development Setup
# Clone the repository
git clone https://github.com/Graham-Schmidt/sitemapy.git
cd sitemapy
# Install dependencies
pip install -e ".[dev]"
# Run tests
pytest
License
This project is licensed under the MIT License - see the LICENSE file for details.
Links
- PyPI: https://pypi.org/project/sitemapy/
- Source Code: https://github.com/Graham-Schmidt/sitemapy
- Issue Tracker: https://github.com/Graham-Schmidt/sitemapy/issues
- Sitemap Protocol: https://www.sitemaps.org/protocol.html
Made with ❤️ by Graham Schmidt
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 sitemapy-0.2.0.tar.gz.
File metadata
- Download URL: sitemapy-0.2.0.tar.gz
- Upload date:
- Size: 9.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ea42015ce7b07546ee89959fa59f0f9e04a1b96edf32d5fc628d101bfeb5bf52
|
|
| MD5 |
3faef70a3de90415dd49bdca2237d90a
|
|
| BLAKE2b-256 |
b23a5dfc541133185c99e75523c04c3a42448d04ba3ab7760d9cea2e53b78338
|
File details
Details for the file sitemapy-0.2.0-py3-none-any.whl.
File metadata
- Download URL: sitemapy-0.2.0-py3-none-any.whl
- Upload date:
- Size: 6.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3ef6d50956cbd39366d85ad3e34d3df9ede56f4d5f83858ff0129742960d0eb5
|
|
| MD5 |
7d7dede2beaadff5ff86d8c4d7c1311e
|
|
| BLAKE2b-256 |
b7a5b52d183947ac475a37f91a23e23ebc720ce2b49eb23327e51f2823cfe0de
|