Last Updated: June 01, 2026
For some of your web apps you develop in python, you will want to run them on the cloud so that your script can run 24/7. For some of your smaller applications, you may want to find the right free python hosting service so you don’t have to worry about the per month charges. These web applications might be a website written in flask, or using another web framework, it might be other types of python apps that runs in the background and runs your automation. This is where you can consider some of the hosting services that have a free plan and are still very easy to setup.
To find the right hosting platforms that fits your needs, you want to consider a few things:
- Ease of access to upload projects
- What type of support they provide
- What specifications that virtual server environment has to offer
One such new platform is called deta.sh. Deta is a free hosting service that can be used to provide web hosting for deploying python web applications or other types of python applications that run in the background.
The deta service, as of mid-2022, is still in the development stage and is expected to have a permanent free python hosting service so that online python applications can be setup and deployed quickly and easily. Deta is a relatively new service but is a service that is intended to compete with pythonanywhere, heroku, and similar services to run python on web servers. The service lets you host python script online without fuss directly from a command line, much like how you can check in code to github. Although it is new, it has the potential to be one of the best free python hosting there is in order to get your python online.
The platform provides you mini virtual environments (called ‘micros’) where you can host your python scripts. These can be separated into workspaces called ‘projects’ so that you can also more easily manage your environments. The way you can access/upload your code is with the command line through a password Access Token.

We will go through step by step how to run your python online. For this article, we will guide you on using deta to host a simple flask based web page so that you can have python as a webserver.
Python developer and educator with 15+ years building production systems across data engineering, web APIs, and AI tooling. Founder of Python How To Program — 270+ in-depth tutorials covering the modern Python stack.
Signing up for Deta.sh
Deta.sh is effectively a cloud python hosting service which sits on top of AWS and allows you to deploy your python code into a virtual machine (called a deta micro), store files (called data drive) and also store data (called deta base). Unlike AWS or other hosting services, you can quickly host and run your script without going through the hassle of setting up server, security configurations etc.
The Deta.sh team offers the service for free in order to allow developers to monetize the solutions where deta.sh will be able to share some of that revenue. To date, there are no paid Deta.sh hosting plans for python hosting and no intention. So you can continue to run python code online forever.
To begin with, head over to the website https://deta.sh to first create an account.

Once you have submitted, go to your email and click on the verify link.

After you click on sign-in, enter the same username and password, and you will be taken to the default page where you will have the ability to “See My Key”

Click on the “See My Key” to see your secret password. You will only be able to see it once and will not be able to see it ever again.
This is what they project key will look like:

You need both the key and the project id.
Think of the key like a password and the “Project ID” as a password. When you want to access your deta.sh to upload programs, make changes, you will need to use your project key to access your space.
If you lose your project id/key, you will not be able to recover it. However, you can create a new one with Settings->Create Key option.

One thing I’d like to call out is the Project ID. This is the ID of this particular s[ace

If you have multiple programs which access deta.sh, it is best to have separate project keys. The reason is that if one of your keys are compromised, then you can simply just change that key and not have all your applications be affected.
Setting Up Your Remote Access For Deta.sh
We will first setup deta.sh in the command line interface so that you can communicate to your deta.sh space on the cloud.
You can do this with either one of:
Mac / Linux:
curl -fsSL https://get.deta.dev/cli.sh | sh
Windows:
iwr https://get.deta.dev/cli.ps1 -useb | iex
Once that’s done, what will happen is that there will be a hidden folder called $HOME/.deta that is created (specifically in the case of Mac / Linux). It’s in this directory that the deta command line application will be found.
You can type deta --help to check that the command line tool was installed correctly

Next, you will need to create an access token so that you can connect to your deta.sh account. For this you will need to create an access token. Go to your deta.sh home page (e.g. https://web.deta.sh/) and then go back to the main projects page.

Next, click on the Create Access token under settings

Once you create token, this will create an Access Token so that you don’t need to login each time.

Copy this Access Token and then, create a file called tokens in the $HOME/.deta/ directory. Steps for Mac/Linux are:
cd $HOME/.deta
nano tokens
You can then add the following json inside the tokens file:
{
"deta_access_token": "<your access token created above>"
}
Finally, you can install the python library that will be used to access the deta components with the deta library.
pip install deta
Have a Free Python Hosting Flask on Deta.sh
To create an environment to host your python code and have python web hosting, you need to create something called a “micro“. This is almost like a mini virtual server with 128mb of memory but will not be running all the time. They will wake up, execute your code, and then go back to sleep. Deta.sh is not designed for long running applications with heavy computations (use one of the public cloud providers for that!). Also, each micro has its own python online cloud private access.
To begin with, you can use the command deta new --python <micro name>. The <micro name> is the name to label the mini-virtual name.

The above command will create a directory called flask_test with a python script called main.py

The default code in the main.py is:
def app(event):
return "Hello, world!"
At the same time, this code will be uploaded to deta.sh. If you go to the dashboard page https://web.deta.sh/ you will see a sub-menu under the Micro menu. You may need to refresh your browser if you had it open.

You will notice that there’s also a URL for this deta micro which is the end point where your application output can be accessed. Think of this simply as the console output.

If you encountered any errors, in the command line, you can type deta logs to get an output of any errors from the logs.
To make a more useful application, we can create a flask application to show a more functional webpage. In order to do this, you will need to dell deta.sh to install the flask library. You cannot use pip install unfortunately, but instead you need to use the requirements.txt instead.
First, add flask into a requirements.txt file in your local directory. So your file should simply look like this:
#requirements.txt
flask
Then in your main.py code file, you add the following, again this is in your local directory
from flask import Flask
app = Flask(__name__)
@app.route('/', methods=["GET"])
def hello_world():
return "Hello Flask World"
# def app(event):
# return "Hello, world!"
In order to now upload the changes to your micro, you will need to run the command deta deploy. This will upload the files requirements.txt and updates to main.py into your micro.
deta deploy
When executed, this should upload the code and install the libraries:

Managing Flask Forms On Free Python Hosting
Now that we have a simple static web page, we can create a more complex example where there’s a form that can be submitted. Using the weather API from openweathermap API, we can show the weather for a given location.
To get the weather data, we need to install two libraries pyowm and datetime. Hence, this will need to be added to requirements.txt.
#requirements.txt
flask
pyowm
datetime
Then for the code, the following can be updated in the main.py:
from flask import Flask, request, jsonify
import pyowm, datetime
app = Flask(__name__)
@app.route('/', methods=["GET"])
def get_location():
return """<html>
<body>
<form action="weather" method="POST">
<input name="location" type="text">
<input type="submit" value="submit">
</form>
</body>
</html>"""
@app.route('/weather', methods=["POST", "GET"])
def get_weather():
api_key = '<your open weather map API ley>'
owm = pyowm.OWM( api_key ).weather_manager()
weather_data = owm.weather_at_place('Bangalore').weather
ref_time = datetime.datetime.fromtimestamp( weather_data.ref_time ).strftime('%Y-%m-%d %H:%M')
weather_str = f"<h1>Weather Report for: {request.form['location']}</h1>"
weather_str += f"<ul>"
weather_str += f"<li><b>Time:</b> { ref_time } </li>"
weather_str += f"<li><b>Overview:</b> {weather_data.detailed_status} </li>"
weather_str += f"<li><b>Wind Speed:</b> {weather_data.wind()} </li>"
weather_str += f"<li><b>Humidity:</b> {weather_data.humidity} </li>"
weather_str += f"<li><b>Temperature:</b> {weather_data.temperature('fahrenheit')} </li>"
weather_str += f"<li><b>Rain:</b> {weather_data.rain} </li>"
weather_str += f"</ul>"
return weather_str
# def app(event):
# return "Hello, world!"
Then to upload the code into deta.sh, you can use the command deploy:
deta deloy
Once deployed, you can then go to the website – this is the endpoint that was automatically generated by deta.sh above.

def get_location()Once submitted, then a call is made to OpenWeatherMap

/ url, then the function def get_weather() is called to process the form. The variable that was passed, can be access through request.form['location']. The above code works by first providing a form through the function def get_location() which generates a very simple form through HTML:
<html>
<body>
<form action="weather" method="POST">
<input name="location" type="text">
<input type="submit" value="submit">
</form>
</body>
</html>
When the submit button is pressed, the form calls the /weather URL with the field location. Once called, then the python function def get_weather() is called upon which a call to OpenWeatherMap.org is made to get the weather data for the given location.
Conclusion
This is just a tip of the iceberg of what you can do with deta. You can also run scheduled jobs, run a NoSQL database, and have file storage as well. Contact us if you’d like us to cover these areas too.
How To Use Python more-itertools for Advanced Iteration
Intermediate
You need to process a list in batches of 50. You need a sliding window over a time series. You need to group consecutive items by a category without sorting the entire list first. The standard library’s itertools module gets you partway there, but you end up writing the same 5-line helper function in every project: the one that chunks a list into fixed-size groups. That function already exists, is battle-tested, and has 100+ siblings in a library called more-itertools.
more-itertools is a pure-Python library that extends the standard itertools with practical, production-ready iteration utilities. It is the most downloaded iteration utility library in the Python ecosystem with tens of millions of downloads per month, and it ships with functions that cover the patterns you repeatedly hand-roll: chunked batching, sliding windows, grouping consecutive items, interleaving sequences, and more. Everything returns lazy iterators, so it handles large sequences without loading them into memory.
In this article, we will cover installing more-itertools, the most useful functions by category, how they compare to hand-written alternatives, and a real-world data pipeline that ties them together. By the end, you will recognize at least five patterns in your current codebase that more-itertools can replace with a single, well-named function call.
more-itertools: Quick Example
The most common use case: splitting a list into fixed-size chunks for batch processing.
# quick_example.py
from more_itertools import chunked
data = list(range(1, 22)) # 21 items
for batch in chunked(data, 5):
print(batch)
Output:
[1, 2, 3, 4, 5]
[6, 7, 8, 9, 10]
[11, 12, 13, 14, 15]
[16, 17, 18, 19, 20]
[21]
chunked(iterable, n) splits any iterable into lists of at most n items. The final batch contains whatever remains — it is not padded. Compare this to writing [data[i:i+5] for i in range(0, len(data), 5)], which only works on sequences with a length; chunked works on any iterable including generators and file objects.
Read on for windowed slices, grouping utilities, interleaving, and a full data pipeline example combining several of these tools.
What Is more-itertools and Why Use It?
more-itertools is a community library maintained as a complement to Python’s built-in itertools module. Where itertools provides the low-level combinatorial primitives (chain, product, combinations), more-itertools focuses on practical, higher-level patterns that appear constantly in real data processing code. Every function returns a lazy iterator compatible with the rest of the itertools ecosystem.
| Function | What it does | stdlib alternative |
|---|---|---|
chunked(it, n) | Split into n-sized lists | Manual slice loop |
windowed(it, n) | Sliding window of size n | Manual deque loop |
grouper(it, n) | Fixed-size tuples, pads last | zip_longest + repeat |
run_length.encode(it) | RLE compression of runs | groupby + len |
flatten(it) | One level of nesting removed | chain.from_iterable |
interleave(*its) | Interleave multiple iterables | zip + chain |
peekable(it) | Look ahead without consuming | No clean stdlib option |
first(it) | First item or default | next() + StopIteration |
The main benefit over writing these yourself is correctness: more-itertools handles edge cases (empty iterables, short final chunks, single-item sequences) that hand-rolled versions often miss. Install once, import by name, never write another chunk-splitter.
Installation
# install.sh
pip install more-itertools
python -c "import more_itertools; print(more_itertools.__version__)"
Output:
10.x.x
No dependencies beyond the Python standard library. The module is imported as more_itertools (underscore, not hyphen). Most production projects import specific functions rather than the whole module to keep imports explicit.
Sliding Windows with windowed()
A sliding window moves one step at a time over a sequence, returning overlapping tuples. This is essential for time series analysis, moving averages, and sequence pattern matching:
# windowed_example.py
from more_itertools import windowed
temperatures = [22, 24, 19, 21, 25, 23, 20, 22]
print("3-day sliding window:")
for window in windowed(temperatures, 3):
avg = sum(window) / len(window)
print(f" {window} -> avg {avg:.1f}")
Output:
3-day sliding window:
(22, 24, 19) -> avg 21.7
(24, 19, 21) -> avg 21.3
(19, 21, 25) -> avg 21.7
(21, 25, 23) -> avg 23.0
(25, 23, 20) -> avg 22.7
(23, 20, 22) -> avg 21.7
The equivalent using only the standard library requires a collections.deque and manual appending — roughly 8 lines. windowed(it, n) reduces this to one line. By default, if the iterable is shorter than the window size, windowed pads missing values with None; pass fillvalue=0 to use a different pad value, or use windowed_complete to skip incomplete windows entirely.
Grouping Consecutive Items
Two functions handle consecutive grouping: run_length.encode() compresses repeated values, and consecutive_groups() finds runs of consecutive integers. Both operate lazily:
# grouping_example.py
from more_itertools import run_length, consecutive_groups
# Run-length encoding: compress repeated values
signals = ['A', 'A', 'A', 'B', 'B', 'A', 'C', 'C', 'C', 'C']
encoded = list(run_length.encode(signals))
print("RLE encoded:", encoded)
decoded = list(run_length.decode(encoded))
print("RLE decoded:", decoded)
print()
# Consecutive groups: group sequential integers
page_numbers = [1, 2, 3, 7, 8, 9, 10, 15, 16]
print("Consecutive page ranges:")
for group in consecutive_groups(page_numbers):
pages = list(group)
if len(pages) == 1:
print(f" Page {pages[0]}")
else:
print(f" Pages {pages[0]}-{pages[-1]}")
Output:
RLE encoded: [('A', 3), ('B', 2), ('A', 1), ('C', 4)]
RLE decoded: ['A', 'A', 'A', 'B', 'B', 'A', 'C', 'C', 'C', 'C']
Consecutive page ranges:
Pages 1-3
Pages 7-10
Page 15
Pages 15-16
run_length.encode() returns (value, count) pairs, while run_length.decode() reverses the process. The consecutive_groups() function detects gaps in integer sequences — useful for summarizing page ranges, time slot gaps, or any integer-indexed data where runs matter.
Lookahead with peekable()
Sometimes you need to inspect the next item in an iterator without consuming it — a common pattern in parsers and state machines. peekable wraps any iterator and adds a .peek() method:
# peekable_example.py
from more_itertools import peekable
def process_stream(items):
it = peekable(items)
results = []
while it:
current = next(it)
# Peek at next item without consuming it
try:
upcoming = it.peek()
if upcoming > current:
results.append(f"{current} (next is higher: {upcoming})")
else:
results.append(f"{current} (next is same/lower: {upcoming})")
except StopIteration:
results.append(f"{current} (last item)")
return results
output = process_stream([3, 5, 5, 2, 8, 1])
for line in output:
print(line)
Output:
3 (next is higher: 5)
5 (next is same/lower: 5)
5 (next is higher: 2)
2 (next is higher: 8)
8 (next is same/lower: 1)
1 (last item)
peekable also supports prepending values back with it.prepend(value), which is useful when you have consumed an item and need to “unread” it. The object is truthy when items remain, so while it: works as a clean loop termination condition. Without peekable, implementing lookahead requires maintaining a separate next_item variable and careful StopIteration handling.
Essential Utility Functions
Here are several more functions that cover common one-off patterns:
# utilities_example.py
from more_itertools import (
flatten, interleave, first, last, one,
unique_everseen, only
)
# flatten: remove one level of nesting
nested = [[1, 2], [3, 4], [5]]
print("flatten:", list(flatten(nested)))
# interleave: merge multiple iterables element by element
evens = [2, 4, 6]
odds = [1, 3, 5]
print("interleave:", list(interleave(evens, odds)))
# first / last: safe access with defaults
items = [10, 20, 30]
print("first:", first(items, default=0))
print("last:", last(items, default=0))
print("first of empty:", first([], default=-1))
# unique_everseen: deduplicate while preserving order
dupes = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3]
print("unique:", list(unique_everseen(dupes)))
# one: assert exactly one item matches a predicate
numbers = [42]
print("one:", one(numbers)) # Returns 42; raises ValueError if 0 or 2+ items
# only: return the single item from a one-element iterable
print("only:", only([99], default=None))
Output:
flatten: [1, 2, 3, 4, 5]
interleave: [2, 1, 4, 3, 6, 5]
first: 10
last: 30
first of empty: -1
unique: [3, 1, 4, 5, 9, 2, 6]
one: 42
only: 99
unique_everseen is particularly useful as an order-preserving deduplication — it keeps the first occurrence of each value, unlike set() which destroys ordering. first and last cleanly handle empty iterables with a default parameter, avoiding the try/next(iter(...))/except StopIteration pattern. one() is a semantic assertion: “I expect exactly one item here” — it raises a clear error if that invariant is violated.
Real-Life Example: Batch API Processor with Progress Tracking
Here is a practical pipeline that uses chunked, windowed, and peekable together to batch-process API requests with rate limiting and look-ahead logging:
# batch_api_processor.py
import time
from more_itertools import chunked, peekable
import urllib.request
import json
def fetch_post(post_id: int) -> dict:
"""Fetch a single post from a test API."""
url = f"https://jsonplaceholder.typicode.com/posts/{post_id}"
with urllib.request.urlopen(url, timeout=5) as resp:
return json.loads(resp.read())
def process_posts_in_batches(post_ids: list[int], batch_size: int = 5):
"""
Fetch posts in batches with rate limiting.
Uses chunked() to group IDs, peekable() to detect the last batch.
"""
batches = peekable(chunked(post_ids, batch_size))
batch_num = 0
while batches:
batch_ids = next(batches)
batch_num += 1
is_last = not batches # peekable: truthy if more items remain
print(f"\nBatch {batch_num}: fetching IDs {batch_ids}")
results = []
for post_id in batch_ids:
post = fetch_post(post_id)
results.append({
"id": post["id"],
"title": post["title"][:40] + "..." if len(post["title"]) > 40 else post["title"],
"user": post["userId"]
})
for r in results:
print(f" [{r['id']}] user={r['user']}: {r['title']}")
if not is_last:
print(" Rate limiting: sleeping 0.5s...")
time.sleep(0.5)
else:
print(" Last batch -- no sleep needed.")
return batch_num
if __name__ == "__main__":
ids_to_fetch = list(range(1, 12)) # 11 posts -> 3 batches of 5, 5, 1
total_batches = process_posts_in_batches(ids_to_fetch, batch_size=5)
print(f"\nDone. Processed {total_batches} batches.")
Output (truncated for brevity):
Batch 1: fetching IDs [1, 2, 3, 4, 5]
[1] user=1: sunt aut facere repellat provident oc...
[2] user=1: qui est esse...
...
Rate limiting: sleeping 0.5s...
Batch 2: fetching IDs [6, 7, 8, 9, 10]
[6] user=1: dolorem eum magni eos aperiam quia...
...
Rate limiting: sleeping 0.5s...
Batch 3: fetching IDs [11]
[11] user=1: et ea vero quia laudantium aute...
Last batch -- no sleep needed.
Done. Processed 3 batches.
The combination of chunked and peekable enables a clean pattern: split work into batches of the right size, then use look-ahead to detect the last batch and skip the unnecessary trailing sleep. The peekable wrapper’s truthiness check (not batches) replaces the awkward “am I on the last iteration?” tracking that normally requires a counter comparison. Swap jsonplaceholder.typicode.com for any real API and adjust the batch size and sleep time for that API’s rate limits.
Frequently Asked Questions
How does more-itertools relate to the stdlib itertools module?
more-itertools is a pure addition, not a replacement. It imports and re-exports everything from itertools, so you can use from more_itertools import chain, chunked to access both standard and extended functions from one import. The library started as a collection of recipes from the official itertools documentation that were commonly hand-written but not included in the standard library, and has grown to 100+ functions over the years.
When should I use chunked() vs grouper()?
Use chunked(it, n) when you want the final partial chunk as a shorter list — the last group might have fewer than n items. Use grouper(it, n, fillvalue=None) when you need all groups to be exactly the same length — the final group is padded with the fill value. For batch database inserts where a short final batch is fine, use chunked. For fixed-width record processing where every row must be the same length, use grouper.
Does more-itertools load everything into memory?
No — like the standard itertools library, most functions return lazy iterators. chunked yields one list at a time; windowed yields one tuple at a time; none of them buffer the entire input. The exception is functions that inherently require buffering, like unique_everseen (which maintains a set of seen values) and last (which must consume the entire iterable). For truly large iterables, stick to the lazy functions and avoid the buffering ones.
Is more-itertools slow compared to hand-written code?
more-itertools is pure Python with no C extensions, so it is not faster than equivalent optimized code. For hot loops processing millions of items, the overhead of Python function calls in the iterator protocol can matter. In those cases, NumPy vectorized operations or Polars expressions will outperform any pure-Python iteration. But for typical batch sizes (hundreds to thousands of items), more-itertools performance is indistinguishable from hand-written loops, and the readability benefit is significant.
Which functions should I learn first?
Start with the ones you hand-write most often: chunked for batching, windowed for sliding analysis, flatten for one level of nesting, first and last for safe endpoint access, and unique_everseen for order-preserving deduplication. Then browse the docs for peekable, one, and consecutive_groups — these cover patterns that are tricky to get right with the standard library and that come up more often than you expect once you know they exist.
Conclusion
In this article, we covered more-itertools from installation through practical use: chunked for batch splitting, windowed for sliding windows, run_length.encode and consecutive_groups for grouping, peekable for look-ahead iteration, and a set of utility functions including flatten, unique_everseen, first, and one. The real-life example combined chunked and peekable in a rate-limited batch API processor.
The consistent theme is that more-itertools names patterns you already use. Once you know chunked exists, you stop writing the slice loop. Once you know peekable exists, you stop maintaining a next_item variable by hand. The library’s value is not raw performance — it is replacing ad-hoc implementations with functions that handle edge cases correctly and that any Python developer can read and understand immediately.
Browse the full function list in the official more-itertools documentation — there are 100+ functions, and a few of them will solve a problem you are currently solving the hard way.
Related Articles
Further Reading: For more details, see the Python virtual environments documentation.
Frequently Asked Questions
Is Deta still free for hosting Python apps?
Deta Space offers a free tier for personal use. The original Deta.sh Micros service has evolved. For free Python hosting alternatives, consider Railway, Render, PythonAnywhere, or Google Cloud Run’s free tier.
What are the best free Python hosting alternatives?
PythonAnywhere offers a free tier for web apps. Render provides free static sites and web services. Railway has a free trial. Google Cloud Run and AWS Lambda have generous free tiers for serverless deployments.
How do I deploy a Python Flask app for free?
Use Render (connect GitHub repo), PythonAnywhere (upload directly), or Railway (deploy from GitHub). Each provides different advantages for hobby and small-scale projects.
What should I consider when choosing Python hosting?
Consider free tier limits, sleep/cold-start behavior, database availability, custom domain support, deployment method, Python version support, and scaling options.
Can I host a Python bot or script for free?
Yes. PythonAnywhere allows always-on tasks. Google Cloud Functions and AWS Lambda handle event-driven scripts. For Discord/Telegram bots, Railway and Render offer free tiers suitable for small bots.
Continue Learning Python
Tutorials you might also find useful: