Annotated version of this introductory video
Datasette is a tool for exploring and publishing data. It helps people take data of any shape, analyze and explore it, and publish it as an interactive website and accompanying API.
Datasette is aimed at data journalists, museum curators, archivists, local governments, scientists, researchers and anyone else who has data that they wish to share with the world. It is part of a wider ecosystem of 44 tools and 154 plugins dedicated to making working with structured data as productive as possible.
Try a demo and explore 33,000 power plants around the world, then follow the tutorial or take a look at some other examples of Datasette in action.
Then read how to get started with Datasette, subscribe to the newsletter and consider signing up for office hours for an in-person conversation about the project.
Exploratory data analysis
Import data from CSVs, JSON, database connections and more. Datasette will automatically show you patterns in your data and help you share your findings with your colleagues.
Instant data publishing
datasette publish lets you instantly publish your data to hosting providers like Google Cloud Run, Heroku or Vercel.
Rapid prototyping
Spin up a JSON API for any data in minutes. Use it to prototype and prove your ideas without building a custom backend.
Latest news and blog
11th September 2026
18th June 2026
11th June 2026
29th May 2026
24th May 2026
21st May 2026
13th May 2026
19th April 2026 #
Here's a new TIL on using SQL functions in Google Sheets to fetch data from Datasette.
15th April 2026 #
Datasette 1.0a27 changes how CSRF protection works in a way that simplifies form and API integration, and introduces a new RenameTableEvent for when a table is renamed by a SQL query.
18th March 2026 #
Datasette 1.0a26 adds a column_types system allowing columns to carry additional type information such as email or url, or extra types defined by plugins. This alpha also adds a UI for selecting and sorting visible columns on a table page.
25th February 2026 #
Datasette 1.0a25 adds write_wrapper() and register_token_handler() plugin hooks.
29th January 2026 #
Datasette 1.0a24 introduces a new await request.form(files=True) method for accepting file uploads, plus various other changes.
5th November 2025 #
Datasette 0.65.2 and Datasette 1.0a21 have been released with a security fix for an open redirect error, see this advisory. The latest Datasette alphas also include a breaking change to Datasette's permission system, described in detail in A new SQL-powered permissions system in Datasette 1.0a20.
6th February 2025 #
Datasette 1.0a17 is the latest Datasette 1.0 alpha release, with bug fixes and small feature improvements from the last few months.
7th October 2024 #
Python 3.13 was released today. Datasette 1.0a16 is compatible with Python 3.13, but Datasette 0.64.8 was not. The new Datasette 0.65 release fixes compatibility with the new version of Python.
Latest releases
11th September 2026
datasette-publish-fly 1.4 - Datasette plugin for publishing data using Fly
- Sets
force_https=trueinfly.toml. #31 - Fix for Volume could not be found bug. #32
- Compatible with app-scoped deploy tokens. #34
github-to-sqlite 2.9.1 - Save data from GitHub to a SQLite database
- Fix for compatibility with sqlite-utils 4.x. #85
datasette 0.65.4 - An open source multi-tool for exploring and publishing data
This release includes security fixes for permissions, SQL construction and caching, backported to the stable 0.65.x branch.
See 1.0a39 for the full set of security fixes and other improvements in the 1.0 alpha series.
The Datasette blog has more details on these releases.
Some of the security fixes include:
- Table and view permission checks now take SQLite's case-insensitive names into account. See Controlling access to specific tables and views.
- Table filters using
?_through=require permission to view the intermediate table. - Fixed SQL identifier escaping for primary-key column names from untrusted database schemas, including row lookups and pagination.
- Full-text search index detection now uses parameterized SQL and treats wildcard characters in table names literally.
- Private and personalized dynamic responses now use
Cache-Control: private, no-store. Anonymous dynamic responses vary byCookieandAuthorization. - SQLite extension loading is disabled after extensions supplied using
--load-extensionhave been loaded.
Other improvements and fixes
- Backported the non-blocking write task ID fixes from #2861 by Zain Dana Harper (HarperZ9).
execute_write_fn(block=False)now returns a unique UUID for each call, including whennum_sql_threads=0.
datasette 1.0a39
See 0.65.4 for fixes that have been backported to the stable 0.65.x branch.
The Datasette blog has more details on these releases.
Some of the security fixes include:
- Table and view permission checks now take SQLite's case-insensitive names into account. See How permissions are resolved.
- Viewing a full-text search index table now checks you have permission to view the table from which it draws its content.
- Viewing SQLite statistics tables (
sqlite_stat1throughsqlite_stat4) is now denied by a default. - Table schema display now obeys the
view-tablepermission. - Table filters using
?_through=require permission to view the intermediate table. - Foreign-key target and suggestion APIs, incoming foreign-key relationships and their row counts now respect
view-tablepermission. - Row endpoints check permissions before resolving primary keys, to avoid revealing the existence of an otherwise invisible primary key.
- Improved permission checks for the create-table API. See The JSON write API.
- The write SQL interface now checks
view-tablepermission for tables referenced byCREATE VIEWstatements. - Fixed SQL identifier escaping for column names from untrusted database schemas.
- Fixed HTML escaping for column names from untrusted database schemas.
- URL columns now render links only for validated HTTP or HTTPS URLs.
- Private and personalized dynamic responses now use
Cache-Control: private, no-store. Anonymous dynamic responses vary byCookieandAuthorization. - Actor cookies now respect
expire_after. - Restricted actors can no longer create API tokens.
- Stored-query create, edit and delete forms now block framing to prevent clickjacking.
- Configuration secret redaction now matches key names case-insensitively.
- SQLite extension loading is disabled after extensions supplied using
--load-extensionhave been loaded.
Other improvements and fixes
- db.execute_write() now has a default execution time limit of 2,000ms. Plugins can override this using
time_limit_ms=or disable it usingtime_limit_ms=None. This limit is independent of thesql_time_limit_mssetting for read queries. - Application startup now runs through ASGI lifespan events before requests are accepted, with a first-request fallback for hosts without lifespan support. Thanks, Alex Garcia. (#2887)
datasette servenow runs startup hooks and Uvicorn on the same event loop, preserving background tasks started by plugins. The minimum Uvicorn version is now 0.29. Thanks, Alex Garcia. (#2886)- Non-blocking writes using
execute_write_fn(..., block=False)now return a distinct task UUID for every call and work correctly withnum_sql_threads=0. Thanks, Zain Dana Harper. (#2860, #2859) - Dropping a table now disables its full-text search index first. (#2874)
- Fixed
CREATE VIEWSQL analysis on Python 3.10.
7th September 2026
llm 0.35 - CLI utility and Python library for interacting with Large Language Models from organizations like OpenAI, Anthropic and Gemini plus local models installed on your own machine.
- New OpenAI model:
gpt-6-astrafor GPT-6 Astra.
2nd September 2026
llm 0.34
New features:
llm logs --usageMarkdown output now includes the response duration in milliseconds and as a human-readable duration.llm logs --shortincludes a newduration_msfield. #1653
Bug fixes:
- Significantly improved the performance of
llm logsfor long conversations by caching repeated message and model lookups. #1654 - Dynamically generated OpenAI options classes are now cached, avoiding repeated Pydantic class construction by plugins such as
llm-openrouter. Thanks, waveplate. #1651 - Invalid schema DSL passed to
llm prompt --schemanow produces a clean command-line error instead of a Python traceback. Thanks, ikatyal2110. #1647 llm --extractnow recognizes fenced code blocks in responses that use CRLF line endings. Thanks, mameikagou. #1644monotonic_ulid()now remains monotonic if the system clock moves backwards or concurrent calls observe timestamps out of order. Thanks, Dylan Pulver. #1641- Declared the
typing-extensionspackage as a direct dependency, and added tests to protect against accidental missing dependencies. Thanks, Vansh Taneja. #1622
22nd August 2026
llm 0.33
New features:
- Upgraded to the OpenAI Python library 3.x and switched the HTTP client dependency from
httpxtohttpx2. #1608, #1631 llm embedandllm embed-multinow accept--key. The PythonEmbeddingModel.embed(),EmbeddingModel.embed_multi(),Collection.embed()andCollection.embed_multi()methods acceptkey=too, passing the resolved per-call key to embedding plugins without changing shared model state. Existing plugins that readself.keycontinue to work through a compatibility fallback. Thanks, ChrisJr404. #757, #1620llm logsnow includes the output of server-side tool calls, shown in a Tool results section within the response. These results are also included inllm logs --jsonandllm logs --shortoutput, with a newserver_executedkey distinguishing them from locally executed tool results. #1629llm prompt -t/--templatecan now be repeated to combine templates in order. This allows model configuration and options from one template to be used with a prompt from another.- Expanded the
llm prompt --helpdocumentation for--schemaand--schema-multiwith details and examples of the supported schema DSL. schema_dsl()now raises descriptiveValueErrorexceptions for unknown field types and duplicate field names, instead of silently treating unknown types as strings or overwriting earlier fields. #1607, #1616- Reasoning stream events that contain provider metadata but no text are now preserved as
ReasoningPartobjects. This allows opaque state such as Anthropic signatures and redacted thinking data to round-trip correctly. simonw/llm-anthropic#81 - Reasoning-capable Responses API models now support a
reasoning_summaryoption withauto,concise, anddetailedvalues. This can be used with llm openai endpoint --responses. #1600 - Conversation prompts now validate that attachments are supported by the selected model before execution, for both synchronous and asynchronous conversations. Thanks, Daniel Peng. #1626, #1628
Bug fixes:
llm logs --data-idsnow setsconversation_idto the ID of the conversation instead of incorrectly duplicating the response ID. Thanks, K Merchant. #1598, #1613- Fixed
llm aliases listraising aValueErrorwhen no aliases are defined. Thanks, Taraka Abhiram. #1602 llm embed-multinow reuses an existing collection's stored embedding model when no default embedding model is configured, and no longer masks unrelatedValueErrorexceptions with a missing-model error. #1523llm tools -m MODELno longer prints a redundant message when the model has no server-side tools.
21st August 2026
llm 0.32.1
This version pins LLM to use OpenAI Python versions less than v3.0.0, ensuring fresh LLM installations work without needing to also install an extra httpx dependency. #1630
13th August 2026
sqlite-utils 4.2.1 - CLI tool and Python library for manipulating SQLite databases
- Fix for
No module named 'typing_extensions'crashing bug accidentally shipped in version 4.2. #842
sqlite-utils 4.2
- New
table.checks,table.column_checksandtable.table_checksintrospection properties expose column-level and table-levelCHECKconstraints. (#834) - New
sqlite_utils.ANYmarker type for creating and introspecting SQLiteANYcolumns. The Python API and CLI can create, add and transform these columns, andtable.transform()andtable.extract()now preserveANYcolumns and their values inSTRICTtables. (#790) table.default_valuesnow unescapes doubled single quotes in string defaults, so a default such as'O''Brien'is returned as"O'Brien". Thanks, ikatyal2110. (#811)table.default_valuesnow decodes unquotedTRUE,FALSEandNULLdefault literals asTrue,FalseandNonerespectively. (#836)table.enable_fts(..., tokenize=...)andsqlite-utils enable-fts --tokenizenow safely quote the tokenizer argument, preventing a crafted value from injecting additional SQL. Thanks, Bunlong Heng. (#828)rows_where(),pks_and_rows_where(),search()andsearch_sql()now supportoffset=without requiringlimit=. Thesqlite-utils rows --offsetoption now works without--limittoo. Thanks, ethanhawkes-gif. (#816, #821)- Empty or whitespace-only input passed to
rows_from_file()is now handled as an empty CSV file instead of raisingcsv.Error. Thanks, Rami Abdelrazzaq. (#808, #837) sqlite-utils convert --dry-runnow works for table and column names containing closing square brackets. (#829)table.indexesandtable.xindexesnow work for table, index and column names containing double quotes. This also fixestable.transform()for tables with those identifiers. Thanks, nyxst4ck. (#824, #825)- Improved type annotations throughout the package and added Pyright regression checks to CI. (#833)
- Changing a
TEXTcolumn toINTEGER,FLOATorREALusingtable.transform()orsqlite-utilstransformnow converts exact empty strings toNULL. Previously they remained empty strings in the numeric column. Thanks, ikatyal2110. (#488, #805)
table.transform() can handle many more edge-cases:
table.transform()now preserves column-level and compositeUNIQUEconstraints, including constraint names, collations, sort order andON CONFLICTbehavior. Renaming columns updates those constraints, while dropping any constituent column removes the entire constraint. (#762)table.transform()now preservesAUTOINCREMENTprimary keys and their sequence high-water marks. Previously a transform removedAUTOINCREMENTand could reuse deleted row IDs. (#602)table.transform()now preservesCHECKconstraints, including comments within their expressions. Renaming a column rewrites identifier references in checks without changing string literals or function names. Dropping a column drops a check owned by that column, and raisesTransformErrorif a remaining check depends on it. (#762)table.transform()now preserves comments immediately before or after column definitions. These comments move with the column if it is renamed or reordered, and are removed if the column is dropped. (#762)table.transform(rename=...)now preserves explicit indexes on renamed columns by dropping and recreating those indexes against the new column names. Previously this raised aTransformError. (#822)table.transform()now works for tables that are referenced by views. Previously theALTER TABLE... RENAME TOstep raisedno such tableif a view referenced the table being transformed. View definitions are left unchanged - see Tables referenced by views. This also fixes a bug wheretransform(keep_table=...)silently rewrote dependent views to point at the frozen backup table instead of the live one. (#831)
11th August 2026
datasette-upload-dbs 0.5a0 - Upload SQLite database files to Datasette
- Documented API for uploading files. #2
- No longer depends on Starlette for file uploads. #13
- Fixed a bug where a corrupt SQLite database could still be swapped in.
datasette-public 0.4a2 - Make specific Datasette tables visible to the public
- Upgraded for compatability with Datasette 1.0a37.
6th August 2026
datasette-auth-tokens 0.4a13 - Datasette plugin for authenticating access using API tokens
- Fix for
sqlite-utils4.0. Thanks, Alex Garcia. #44
datasette 1.0a38 - An open source multi-tool for exploring and publishing data
This release fixes a SQL injection security issue that affects Datasette instances that serve a mixture of public and private tables in the same database, with access configured using the Datasette permissions system.
Site administrators who serve private tables in this way are advised to disable the execute-sql permission permission on that database to prevent users from accessing private tables using raw SQL queries. The bug that has been fixed would have allowed users with access to any public table to execute SQL injection attacks despite that restriction, giving them read-only access to data in private tables in the same database.
This fix is also available in Datasette 0.65.3.
datasette 0.65.3
This release fixes a SQL injection security issue that affects Datasette instances that serve a mixture of public and private tables in the same database, with access configured using the Datasette permissions system.
Site administrators who serve private tables in this way are advised to disable the execute-sql permission on that database to prevent users from accessing private tables using raw SQL queries. The bug that has been fixed would have allowed users with access to any public table to execute SQL injection attacks despite that restriction, giving them read-only access to data in private tables in the same database.
This fix is also available in Datasette 1.0a38.