← Blog

How to build a numerology app in Python (with a free API)

·4 min read

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/cycles with a birthdate and "years_ahead": 3 returns the personal year, month and day, plus the next three years. Use it for a “your year ahead” feature.
  • Compatibility: POST /v1/compatibility with two people (a and b) compares their charts for dating or friendship features.
  • Bulk import: POST /v1/charts/batch computes 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.

Get a free key

Keep reading