Developers

household.email API

Your household's calendar, todos, chores and shopping lists over HTTP — and the same data over the Model Context Protocol, so an AI assistant can read and update it directly. One API key, scoped to one household, with per-resource permissions you choose.

API access is a Pro feature

Every endpoint below refuses a household on the free plan, whatever the key's permissions say. The two sign-in endpoints are the exception: they take no API key, so the native apps can sign in on any plan. See pricing.

Quick start

Create a key on the API keys page (Integrations → API keys) and pick, per resource, whether the key gets no access, read-only or read and write. The key is shown once, at creation; its permissions can be changed later without reissuing it. Then check it works:

curl -H "Authorization: Bearer YOUR_API_KEY" \
     "https://household.email/api/me"

Which answers with the household the key belongs to:

{
  "household": { "id": "0f2c…", "name": "The Cipollas" },
  "user": { "id": "8ab1…", "email": "you@example.com", "initials": "MC" },
  "api_key": { "name": "Home Assistant", "permissions": ["calendar:read", "todos"] }
}

Base URL

https://household.email/api

Conventions

  • Request and response bodies are JSON. Send Content-Type: application/json on writes.
  • Write bodies are wrapped in the resource name: {"todo": {…}}, {"event": {…}}, {"shopping_list": {…}}, {"item": {…}}.
  • IDs are UUIDs. Timestamps are ISO 8601; dates are YYYY-MM-DD.
  • Updates accept PATCH or PUT and take only the fields you are changing.
  • Deletes answer 204 No Content with an empty body.
  • A key belongs to a household, not to a person. Records created through the API are attributed to the household's representative adult.

Authentication

Every request carries the key in the Authorization header. Both forms are accepted:

Authorization: Bearer YOUR_API_KEY
Authorization: YOUR_API_KEY

Keys are stored hashed, so a lost key cannot be recovered — delete it and make another. A key can be given an expiry when it is created; an expired key is rejected the same way an unknown one is. Keep keys out of version control.

Issuing a key from an app: the magic-link flow

An app that signs a household member in — rather than being configured with a key by hand — can mint its own key in two steps. This is the flow the native iOS and Android shells use. Neither step needs an existing key.

POST /api/auth/magic_link

Email a sign-in link to a household member.

Requires None

Body

Name Type Required Description
email string Yes The member's email address. Matched case-insensitively.
env_label string No Label shown in the email, so a test build's link is distinguishable from production's.
curl -X POST \
     -H "Content-Type: application/json" \
     -d '{"email":"you@example.com"}' \
     "https://household.email/api/auth/magic_link"

{ "sent": true }

The answer is always {"sent": true}, even for an address that has no account. That is deliberate: the endpoint is unauthenticated, and it must not tell a stranger who has an account here.

POST /api/auth/exchange

Trade the token from the emailed link for a new API key.

Requires None

Body

Name Type Required Description
token string Yes The token from the magic link. Single use, and valid for 30 minutes.
device_label string No Names the key, as "Mobile — <label>". Defaults to "unnamed device".
curl -X POST \
     -H "Content-Type: application/json" \
     -d '{"token":"THE_TOKEN","device_label":"iPhone"}' \
     "https://household.email/api/auth/exchange"

{
  "api_key": "3f9c…",
  "household": { "id": "0f2c…", "name": "The Cipollas" },
  "user": { "id": "8ab1…", "email": "you@example.com", "initials": "MC" }
}

The key comes back once and is not retrievable afterwards. It is granted read and write on every resource, so treat it like the sign-in it stands in for. Replaying a token, using one older than 30 minutes, or sending a token that was never issued all answer 401 with {"error": "Invalid or expired token"}. A member with no household gets 422.

GET /api/me

The household, the person the key belongs to, and the key's permissions.

Requires Any active key

The one endpoint that ignores permissions: any active key on a Pro household can call it. Use it to check a key is live and to find out what it may do before trying it.

Permissions

A key carries a comma-separated list of permissions. The key form offers, per resource, no access, read-only, or read and write:

Resource Read-only Read & write
calendar calendar:read calendar
todos todos:read todos
chores chores:read chores
shopping_lists shopping_lists:read shopping_lists
recipes recipes:read Read-only resource
meal_plans meal_plans:read Read-only resource
emails emails Read-only resource

A coarse permission implies every fine-grained one below it: a key holding calendar satisfies calendar:read, calendar:create, calendar:update and calendar:delete. Calendar and todos can also be granted one verb at a time by writing the fine-grained permission on its own.

Two exceptions are worth knowing before you build against them. The REST endpoints for chores and shopping lists check the coarse chores and shopping_lists permissions for reads as well as writes, so a read-only key on those resources cannot call them over HTTP — it can still read them through the MCP tools, which check chores:read and shopping_lists:read. And emails grants no REST endpoints at all; it exists so an MCP client can search your inbox.

A key never reaches beyond the household it was created in. Asking for another household's record answers 404, not 403.

Endpoint index

Every endpoint the API serves, and the permission each one checks.

Method Path Permission What it does
Authentication
POST /api/auth/magic_link None Email a sign-in link to a household member.
POST /api/auth/exchange None Trade a magic-link token for a new API key.
Identity
GET /api/me Any active key The household, the person the key belongs to, and the key's permissions.
Glance
GET /api/glance Any active key Today at a glance, for home-screen widgets: one payload carrying the sections the key may read, each with the summary line the app's own tiles show. Answers a repeat request with a 304.
Calendar
GET /api/calendar calendar:read Events starting in a date range.
GET /api/calendar/:id calendar:read One event.
POST /api/calendar calendar:create Create an event.
PATCH /api/calendar/:id calendar:update Update an event.
DELETE /api/calendar/:id calendar:delete Delete an event.
Todos
GET /api/todos todos:read The household's todos, optionally filtered.
GET /api/todos/:id todos:read One todo.
POST /api/todos todos:create Create a todo.
PATCH /api/todos/:id todos:update Update a todo.
DELETE /api/todos/:id todos:delete Delete a todo.
Chores
GET /api/chores chores Chores with whose turn it is, plus the last week of completions.
POST /api/chores/:id/complete chores Record a completion and roll the chore forward.
Shopping lists
GET /api/shopping_lists shopping_lists Every list with its items.
GET /api/shopping_lists/:id shopping_lists One list with its items.
POST /api/shopping_lists shopping_lists Create a list.
PATCH /api/shopping_lists/:id shopping_lists Rename a list.
DELETE /api/shopping_lists/:id shopping_lists Delete a list and its items.
POST /api/shopping_lists/:shopping_list_id/items shopping_lists Add an item to a list.
PATCH /api/shopping_lists/:shopping_list_id/items/:id shopping_lists Update an item.
PATCH /api/shopping_lists/:shopping_list_id/items/:id/toggle shopping_lists Tick an item off, or put it back on.
DELETE /api/shopping_lists/:shopping_list_id/items/:id shopping_lists Remove an item.
Recipes
GET /api/recipes recipes:read The recipe book in title order, without the lines or the method.
GET /api/recipes/:id recipes:read One recipe in full, with every ingredient line and the method.
Meal plan
GET /api/meal_plan_entries meal_plans:read The meals planned between two dates, in date and meal order.
MCP
GET, POST, DELETE /api/mcp Per tool Model Context Protocol endpoint, Streamable HTTP transport.

Calendar

GET /api/calendar

Events starting between two dates, inclusive of both days.

Requires calendar:read

Query parameters

Name Type Required Description
start date Yes First day of the window, YYYY-MM-DD.
end date Yes Last day of the window, YYYY-MM-DD. Must not be before start.

Both are required: a missing, unparseable or reversed range answers 400. Events are matched on their own start_at; a recurring series is returned once, as the series, rather than expanded into occurrences.

curl -H "Authorization: Bearer YOUR_API_KEY" \
     "https://household.email/api/calendar?start=2026-09-01&end=2026-09-30"

{
  "events": [ { … } ],
  "meta": { "start": "2026-09-01", "end": "2026-09-30", "count": 12 }
}

GET /api/calendar/:id

One event, wrapped as { "event": { … } }.

Requires calendar:read

POST /api/calendar

Create an event. Answers 201 with the created event.

Requires calendar:create

Body, under event

Name Type Required Description
title string Yes Event title.
start_at datetime Yes When it starts, ISO 8601.
end_at datetime No When it ends, ISO 8601.
description string No Free text.
all_day boolean No Whether it takes the whole day.
location string No Where it is.
event_type string No One of event, birthday, anniversary. Defaults to event.
rrule string No iCalendar recurrence rule, e.g. FREQ=WEEKLY;BYDAY=TU.
curl -X POST \
     -H "Authorization: Bearer YOUR_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{"event":{"title":"Swimming","start_at":"2026-09-15T16:00:00Z","end_at":"2026-09-15T17:00:00Z","location":"Aquatic centre"}}' \
     "https://household.email/api/calendar"

PATCH /api/calendar/:id

Update an event. Send only the fields you are changing.

Requires calendar:update

DELETE /api/calendar/:id

Delete an event. Answers 204.

Requires calendar:delete

Event shape

{
  "event": {
    "id": "9a1e…",
    "title": "Swimming",
    "description": null,
    "start_at": "2026-09-15T16:00:00Z",
    "end_at": "2026-09-15T17:00:00Z",
    "all_day": false,
    "event_type": "event",
    "location": "Aquatic centre",
    "rrule": null,
    "from_todo": false,
    "created_at": "2026-09-01T09:12:41Z",
    "updated_at": "2026-09-01T09:12:41Z"
  }
}

from_todo marks an event the app created from a todo's due date, so a client can tell it apart from one somebody entered.

Todos

GET /api/todos

The household's todos in display order.

Requires todos:read

Query parameters

Name Type Required Description
status string No active, completed or cancelled.
priority string No low, medium, high or urgent.
curl -H "Authorization: Bearer YOUR_API_KEY" \
     "https://household.email/api/todos?status=active&priority=urgent"

{ "todos": [ { … } ], "meta": { "count": 3 } }

GET /api/todos/:id

One todo, wrapped as { "todo": { … } }.

Requires todos:read

POST /api/todos

Create a todo. Answers 201 with the created todo.

Requires todos:create

Body, under todo

Name Type Required Description
title string Yes What needs doing.
description string No Free text.
due_date datetime No When it is due, ISO 8601.
all_day boolean No Whether the due date is a whole day rather than a time.
priority string No low, medium, high or urgent. Defaults to medium.
status string No active, completed or cancelled. Defaults to active.
recurrence string No daily, weekly, fortnightly, monthly or yearly. Omit for a one-off; completing a repeating todo creates the next occurrence with the same assignees.
auto_create_calendar_event boolean No Also put the due date on the household calendar.
curl -X POST \
     -H "Authorization: Bearer YOUR_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{"todo":{"title":"Pay the water bill","priority":"high","due_date":"2026-09-30T00:00:00Z"}}' \
     "https://household.email/api/todos"

PATCH /api/todos/:id

Update a todo. Send only the fields you are changing.

Requires todos:update

DELETE /api/todos/:id

Delete a todo. Answers 204.

Requires todos:delete

Todo shape

{
  "todo": {
    "id": "4c77…",
    "title": "Pay the water bill",
    "description": null,
    "status": "active",
    "priority": "high",
    "assignment_type": "unassigned",
    "recurrence": null,
    "due_date": "2026-09-30T00:00:00Z",
    "position": 1,
    "assigned_adult_ids": [],
    "overdue": false,
    "from_email": false,
    "created_at": "2026-09-01T09:12:41Z",
    "updated_at": "2026-09-01T09:12:41Z"
  }
}

assignment_type is unassigned, single or multiple (two or more assignees — todos do not rotate), and from_email marks a todo the app pulled out of a message. Assignments are read-only here: the API reports assigned_adult_ids but does not set them.

Chores

Chores are recurring, and can rotate between household members. Both endpoints need the coarse chores permission.

GET /api/chores

Chores by next due date, with whose turn it is worked out for you.

Requires chores

Query parameters

Name Type Required Description
overdue boolean No Pass true for only the chores past their due date.
due_today boolean No Pass true for only the chores due today. Ignored if overdue=true.
curl -H "Authorization: Bearer YOUR_API_KEY" \
     "https://household.email/api/chores?due_today=true"

{
  "chores": [
    {
      "id": "b410…",
      "title": "Take the bins out",
      "description": null,
      "frequency": "weekly",
      "frequency_label": "Weekly",
      "next_due_date": "2026-09-15",
      "formatted_next_due_date": "Tue 15 Sep",
      "has_rotation": true,
      "current_assignee_id": "8ab1…",
      "current_assignee_name": "Mark",
      "rotation_assignees": [ { "id": "8ab1…", "name": "Mark", "type": "Adult" } ],
      "overdue": false,
      "position": 0,
      "created_at": "2026-08-01T09:12:41Z",
      "updated_at": "2026-09-08T20:03:02Z"
    }
  ],
  "recent_completions": [
    {
      "id": "d902…",
      "chore_id": "b410…",
      "chore_title": "Take the bins out",
      "completer_id": "8ab1…",
      "completer_name": "Mark",
      "completer_type": "Adult",
      "completed_at": "2026-09-08T20:03:02Z"
    }
  ],
  "meta": { "count": 1 }
}

recent_completions covers the last seven days, newest first, across every chore in the household — not just the ones in chores. frequency is one of daily, weekly, fortnightly or monthly.

POST /api/chores/:id/complete

Record a completion and move the chore to its next due date.

Requires chores

Body

Name Type Required Description
completer_id uuid No Who did it. Defaults to the household's representative adult.
completer_type string No Adult or Child. Required alongside completer_id.
create_todo boolean No Whether to create a todo for whoever's turn is next. Defaults to true; pass "false" to skip it.
curl -X POST \
     -H "Authorization: Bearer YOUR_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{"completer_id":"8ab1…","completer_type":"Adult","create_todo":"false"}' \
     "https://household.email/api/chores/b410…/complete"

{ "chore": { … }, "completion": { … } }

Chores are read and completed through the API but not created, changed or deleted by it; use the app for that. A completer that does not belong to the household answers 422 with {"error": "No completer available"}.

Shopping lists

Every endpoint here checks the coarse shopping_lists permission, reads included.

GET /api/shopping_lists

Every list, in display order, each with its items inline.

Requires shopping_lists

GET /api/shopping_lists/:id

One list with its items.

Requires shopping_lists

POST /api/shopping_lists

Create a list. Answers 201.

Requires shopping_lists

Body, under shopping_list

Name Type Required Description
name string Yes What the list is called.

PATCH /api/shopping_lists/:id

Rename a list.

Requires shopping_lists

DELETE /api/shopping_lists/:id

Delete a list and everything on it. Answers 204.

Requires shopping_lists

POST /api/shopping_lists/:shopping_list_id/items

Add an item to a list. Answers 201.

Requires shopping_lists

Body, under item

Name Type Required Description
name string Yes What to buy.
quantity string No Free text, e.g. "2 kg" or "12".
checked boolean No Whether it is already in the trolley.
label_names array of strings No Shop or aisle labels. Labels that do not exist yet are created.
curl -X POST \
     -H "Authorization: Bearer YOUR_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{"item":{"name":"Milk","quantity":"2 L","label_names":["Woolworths"]}}' \
     "https://household.email/api/shopping_lists/7d31…/items"

PATCH /api/shopping_lists/:shopping_list_id/items/:id

Update an item.

Requires shopping_lists

PATCH /api/shopping_lists/:shopping_list_id/items/:id/toggle

Flip an item between bought and not bought. Takes no body.

Requires shopping_lists

DELETE /api/shopping_lists/:shopping_list_id/items/:id

Remove an item. Answers 204.

Requires shopping_lists

List shape

{
  "shopping_list": {
    "id": "7d31…",
    "name": "Groceries",
    "primary": true,
    "position": 0,
    "items": [
      {
        "id": "c55a…",
        "shopping_list_id": "7d31…",
        "name": "Milk",
        "quantity": "2 L",
        "checked": false,
        "labels": ["Woolworths"],
        "position": 0,
        "created_at": "2026-09-10T07:41:00Z",
        "updated_at": "2026-09-10T07:41:00Z"
      }
    ],
    "items_count": 1,
    "checked_count": 0,
    "created_at": "2026-08-02T11:00:00Z",
    "updated_at": "2026-09-10T07:41:00Z"
  }
}

The collection endpoint wraps the same objects as {"shopping_lists": [ … ], "meta": {"count": n}}, and the item endpoints answer {"item": { … }}.

Recipes

Read-only. Recipes are written, imported and structured in the app — that is a workflow rather than a single request — so the API reads the book without writing to it. Only recipes:read exists as a permission; there is no coarse recipes, deliberately, so a key granted today cannot silently widen into write access the day write endpoints land.

These endpoints check two plan features, not one: API access, and meal planning. A Pro household has both; a household with API access but not meal planning gets 403 with {"error": "Plan upgrade required"}.

GET /api/recipes

The recipe book in title order, without the ingredient lines or the method.

Requires recipes:read

Query parameters

Name Type Required Description
q string No Narrow by keyword. Matches titles, descriptions and ingredients.
category string No Narrow to one kind of dish. One of breakfast, lunch, dinner, side, snack, dessert, cake, drink, sauce. An unrecognised value returns no recipes rather than all of them.
curl -H "Authorization: Bearer YOUR_API_KEY" \
     "https://household.email/api/recipes?q=aubergine"

{ "recipes": [ { … } ], "meta": { "count": 2 } }

GET /api/recipes/:id

One recipe in full: the summary, the method, and every ingredient line.

Requires recipes:read

{
  "recipe": {
    "id": "e18f…",
    "title": "Bolognese",
    "description": "The Sunday one.",
    "servings": 4,
    "prep_minutes": 15,
    "cook_minutes": 120,
    "total_minutes": 135,
    "source_url": "https://example.com/bolognese",
    "source_name": "example.com",
    "source_label": "example.com",
    "ingredients_count": 12,
    "categories": ["dinner"],
    "category_labels": ["Dinner"],
    "allergens": [
      { "allergen": "milk", "label": "Milk", "ingredients": ["100 ml whole milk"] }
    ],
    "instructions": "Soffritto first…",
    "ingredients": [
      {
        "id": "aa20…",
        "text": "2 cups finely chopped onion, divided",
        "quantity": "2",
        "unit": "cups",
        "food_name": "onion",
        "note": "finely chopped, divided",
        "position": 0,
        "allergens": []
      }
    ],
    "created_at": "2026-09-02T18:20:00Z",
    "updated_at": "2026-09-02T18:20:00Z"
  }
}

Each ingredient keeps both forms: text is the line as a person reads it and is always present, while quantity, unit and food_name are filled in only where they could be parsed out of it. A line nothing could be parsed from still arrives, unstructured — so scale arithmetic has to fall back to text rather than assume a quantity is there.

Categories

A recipe carries several categories, not one — a soup is lunch and dinner, a lemon drizzle is a cake and a dessert — so categories is an array and is frequently empty: nothing obliges a household to file its recipes. Treat an empty array as "not filed", never as a kind of its own, and do not expect ?category= to return it.

The vocabulary is closed, so a filter can be relied on to mean the same thing in every household: breakfast, lunch, dinner, side, snack, dessert, cake, drink, and sauce. category_labels is the same list as it reads in the interface, sent alongside so a client displaying them does not have to keep its own copy of the vocabulary in step with ours.

Allergens

Both the summary and the full recipe carry an allergens array: the allergens an ingredient's name matched, each with the lines responsible, so a reader can check the match instead of taking it on trust. The asymmetry matters and the API will not paper over it — a match is a suspicion worth raising; an empty array is not an assurance that a recipe is free from anything. Matching names cannot establish that. Do not present an empty array as "free from".

Meal plan

Read-only, and gated on meal planning as well as API access, exactly as recipes are. A week is not a record — every entry carries its own date — so there is no plan resource to fetch, only the entries between two days.

GET /api/meal_plan_entries

The meals planned between two dates, in date and meal order.

Requires meal_plans:read

Query parameters

Name Type Required Description
start_date date No First day to include, YYYY-MM-DD. Give both dates or neither.
end_date date No Last day to include, YYYY-MM-DD. Omit both for this week, Monday to Sunday.

One date without the other is an error rather than a half-open range, and a date that will not parse is rejected rather than quietly falling back to this week — a client that sent one asked for a range it is not getting. Both answer 400: "start_date and end_date must be dates formatted YYYY-MM-DD", or "start_date must be on or before end_date" for a backwards range.

curl -H "Authorization: Bearer YOUR_API_KEY" \
     "https://household.email/api/meal_plan_entries?start_date=2026-09-14&end_date=2026-09-20"

{
  "meal_plan_entries": [
    {
      "id": "5b7c…",
      "date": "2026-09-15",
      "meal_type": "dinner",
      "title": "Bolognese",
      "servings": 6,
      "spare_servings": 4,
      "spare_remaining": 2,
      "scale_factor": 2.5,
      "leftover": false,
      "leftover_of_id": null,
      "cooked_on": null,
      "use_by_date": null,
      "recipe": { "id": "e18f…", "title": "Bolognese", "allergens": [] },
      "created_at": "2026-09-10T08:00:00Z",
      "updated_at": "2026-09-10T08:00:00Z"
    }
  ],
  "meta": { "start_date": "2026-09-14", "end_date": "2026-09-20", "count": 1 }
}

meal_type is breakfast, lunch, snack, dinner or dessert. An entry can stand on its own without a recipe — "Takeaway", "Leftovers" and "Eating at Nan's" are real parts of a week — so recipe may be null, and scale_factor is null with it: there is nothing to scale. title is always populated — the entry's own name where it has one, otherwise the recipe's.

servings is what that sitting makes, which can differ from the recipe's own count. The nested recipe is the same summary shape GET /api/recipes returns, allergens included, so a whole week can be checked against a household's restrictions in one request.

spare_servings is what the meal makes on top of that sitting, for the fridge, and spare_remaining is how much of it nothing later has claimed yet — "how many serves of the bolognese are left" in one field. scale_factor is the two counts together over the recipe's own, because what is cooked spare is still cooked: the example above is six for the table and four for the fridge, from a recipe written for four.

An entry with leftover_of_id set is eating out of that meal rather than being cooked again, and takes its cooked_on from it. use_by_date on a leftovers entry is four days from cooked_on; it is informational, and nothing validates, hides or expires against it.

MCP (AI assistants)

The same data is served over the Model Context Protocol, so an assistant that speaks MCP can work with your household directly. It authenticates with the same API keys, over the Streamable HTTP transport:

https://household.email/api/mcp

The key's permissions decide which tools the assistant is even offered — a read-only key gets the list and search tools and nothing else, so an assistant cannot change what it was never given access to.

Tool Required permission
search_household Any read permission. It searches only the sources the key can read — emails need emails.
list_calendar_eventscalendar:read
list_todostodos:read
list_choreschores:read
list_shopping_listsshopping_lists:read
list_recipes, get_reciperecipes:read, plus a plan including meal planning
list_meal_plan_entriesmeal_plans:read, plus a plan including meal planning
create_calendar_event, update_calendar_event, delete_calendar_eventcalendar:create, calendar:update, calendar:delete
create_todo, update_todo, delete_todotodos:create, todos:update, todos:delete
add_shopping_item, check_shopping_item, remove_shopping_itemshopping_lists:create, shopping_lists:update, shopping_lists:delete
complete_chorechores

Email content is written by people outside your household, so the server tells assistants to treat it as information and never as instructions. Grant emails only to assistants you would trust with your inbox.

A household on the free plan gets a JSON-RPC error with code -32003 and HTTP 403, rather than the REST API's bare error object, so an MCP client can read it.

Connect your assistant

Run in a terminal:

claude mcp add --transport http household-email https://household.email/api/mcp \
  --header "Authorization: Bearer YOUR_API_KEY"

Errors

Failures answer with a JSON object. Everything except a validation failure uses a single error string; validation failures use errors, an array of sentences fit to show a person.

Status When Body
400 A required parameter is missing or cannot be parsed. {"error": "Both 'start' and 'end' parameters are required (format: YYYY-MM-DD)"}
401 No Authorization header, or a key that is unknown, deleted or expired. {"error": "API key is missing"} or {"error": "Invalid or expired API key"}
403 The household is not on Pro. {"error": "Plan upgrade required"}
403 The plan is fine, but the key lacks the permission. {"error": "API key does not have permission to access calendar:create"}
404 No such record in this household — including records that exist in someone else's. {"error": "Resource not found"}
422 A create or update failed validation. {"errors": ["Title can't be blank"]}

The plan check runs before the permission check, so a free household is told about its plan rather than about the key's scopes. Both are 403; the message is what tells them apart.

Rate limits

There are none today. No request budget is enforced and no rate-limit headers are returned, so please be reasonable: poll on a sensible interval and back off on errors. If we add limits we will publish them here before they take effect.

Questions

Email hello@household.email. We're small, so that address reaches a person who can answer.