How to build a numerology app in Python (with a free API)
This tutorial builds a small web app in Python: a form that takes a name and a date of birth and shows that person’s numerology chart. It takes about fifteen minutes and uses the free tier of the Simple Numerology API, so it costs nothing to follow.
You will need Python 3.10 or later and a free API key from the pricing section. No card is required.
1. Set up the project
mkdir numerology-app && cd numerology-app
python -m venv .venv && source .venv/bin/activate
pip install flask requests
export NUMEROLOGY_API_KEY=nk_live_…
Create a separate key for development from the dashboard, so you can revoke it later without touching the one your deployed app uses.
2. Get a chart
The API is one POST with a JSON body. A Pythagorean chart needs a first name, a last name and a birthdate. A middle name is optional but changes the full-name numbers, so ask for it.
import os
import requests
chart = requests.post(
"https://simplenumerologyapi.com/v1/charts/pythagorean",
headers={"X-API-Key": os.environ["NUMEROLOGY_API_KEY"]},
json={"first_name": "María", "middle_name": "Stefania", "last_name": "García López", "birthdate": "1990-03-12"},
timeout=10,
).json()
chart["life_path_number"] # 7
chart["destiny_number"] # 5
chart["soul_urge_number"] # 4
The response is a JSON object with 47 fields. The chart reference lists them all.
3. The Flask app
Create app.py:
import os
from functools import lru_cache
import requests
from flask import Flask, render_template_string, request
API = "https://simplenumerologyapi.com/v1/charts/pythagorean"
HEADERS = {"X-API-Key": os.environ["NUMEROLOGY_API_KEY"]}
app = Flask(__name__)
class ChartError(Exception):
pass
PAGE = """
<form method="post">
<input name="first" placeholder="First name" required>
<input name="middle" placeholder="Middle name">
<input name="last" placeholder="Last name" required>
<input name="birthdate" type="date" required>
<button>Calculate</button>
</form>
{% if error %}<p>{{ error }}</p>{% endif %}
{% if chart %}
<h2>{{ chart.person.first_name }}'s chart</h2>
<ul>
<li>Life path: {{ chart.life_path_number }}</li>
<li>Destiny: {{ chart.destiny_number }}</li>
<li>Soul urge: {{ chart.soul_urge_number }}</li>
<li>Personality: {{ chart.personality_number }}</li>
<li>Karmic debts: {{ chart.karmic_debt_numbers or "none" }}</li>
</ul>
{% endif %}
"""
@lru_cache(maxsize=10_000)
def chart_for(first, middle, last, birthdate):
# Charts are deterministic, so the same input can be cached forever.
# Errors raise, so they are never cached.
r = requests.post(
API,
headers=HEADERS,
timeout=10,
json={
"first_name": first,
"middle_name": middle,
"last_name": last,
"birthdate": birthdate,
},
)
body = r.json()
if not r.ok:
raise ChartError(body["error"]["message"])
return body
@app.route("/", methods=["GET", "POST"])
def index():
chart = error = None
if request.method == "POST":
f = request.form
try:
chart = chart_for(f["first"].strip(), f.get("middle", "").strip(), f["last"].strip(), f["birthdate"])
except ChartError as e:
error = str(e)
return render_template_string(PAGE, chart=chart, error=error)
Run it with flask run and open http://127.0.0.1:5000.
4. Things that trip people up
Master numbers. Most fields are 1–9, but 11, 22 and 33 are kept rather than reduced. If you store numbers in a database, don’t constrain the column to one digit, and give master numbers their own copy. See master numbers and your integer column.
Arrays. hidden_passion_number is a list, because two numbers can tie.
karmic_debt_numbers and karmic_lesson_numbers can be empty.
Validation. An impossible date such as 1990-02-30 returns a 422 whose
error.param names the field that was wrong. Show the message next to that form
field.
Keys stay on the server. The API sends no CORS headers, on purpose. Never put a key in front-end code.
5. Caching properly
lru_cache is fine for a demo. In production, store the chart next to the user
when they sign up. A chart never changes for the same input and the same
conventions, and every response includes a conventions object recording how
it was computed. Store that too, and you’ll always know which rules produced a
stored chart.
6. Going further
- Personal year:
POST /v1/cycleswith a birthdate and"years_ahead": 3returns the personal year, month and day, plus the next three years. Use it for a “your year ahead” feature. - Compatibility:
POST /v1/compatibilitywith two people (aandb) compares their charts for dating or friendship features. - Bulk import:
POST /v1/charts/batchcomputes up to 100 charts in one request.
The Python quickstart covers error handling and every endpoint in more detail.
Compute this properly, in one request
47 Pythagorean fields from a name and a date of birth, to the conventions described above. 100 charts a month free, no card. Quickstarts for Python and JavaScript.
Keep reading
Adding numerology to your app in about five lines
A practical guide to integrating a numerology chart endpoint — the request, the response shape, the three fiel…
How to calculate a Life Path number (and the mistake almost everyone makes)
The Life Path number is the most-quoted figure in Pythagorean numerology, and the shortcut most calculators us…