Request timeouts

Web endpoint (a.k.a. webhook) requests should complete quickly, ideally within a few seconds. All web endpoint function types (web_endpoint, asgi_app, wsgi_app) have a maximum HTTP request timeout of 150 seconds enforced. However, the underlying Modal function can have a longer timeout.

In case the function takes more than 150 seconds to complete, a HTTP status 303 redirect response is returned pointing at the original URL with a special query parameter linking it that request. This is the result URL for your function. Most web browsers allow for up to 20 such redirects, effectively allowing up to 50 minutes (20 * 150 s) for web endpoints before the request times out.

Some libraries and tools might require you to add a flag or option in order to follow redirects automatically, e.g. curl -L ... or http --follow ....

The result URL can be reloaded without triggering a new request. It will block until the request completes.


In cases when you want a web endpoint to trigger and return the result URL immediately, you can add the wait_for_response=False flag to your web_endpoint/asgi_app/wsgi_app decorator. The decorated function will then be immediately triggered and a 202 Accepted status code will be returned, along with a JSON payload containing the result URL: {"result_url": "..."}.

This can be useful if you for example want to store the result URL somewhere for later access, or immediately redirect a web browser to a URL that won’t trigger a new function call on manual refresh.

Note that once the body is returned from the result URL and is sent successfully as an HTTP response, it may not be accessed again. This is because HTTP has stream semantics, and we support large response bodies. (For responses less than a few MiB, it may work briefly, but this is not supported.)

Polling solutions

Sometimes it can be useful to be able to poll for results rather than wait for a long running HTTP request. The easiest way to do this is to have your web endpoint spawn a modal.Function call and return the function call id that another endpoint can use to poll the submitted function’s status. Here is an example:

import fastapi
from modal import Stub, asgi_app
from modal.functions import FunctionCall

stub = Stub()

web_app = fastapi.FastAPI()

def fastapi_app():
    return web_app

def slow_operation():
async def accept_job(request: fastapi.Request):
    call = slow_operation.spawn()
    return {"call_id": call.object_id}

async def poll_results(call_id: str):
    function_call = FunctionCall.from_id(call_id)
        return function_call.get(timeout=0)
    except TimeoutError:
        http_accepted_code = 202
        return fastapi.responses.JSONResponse({}, status_code=http_accepted_code)

Document OCR Web App is an example that uses this pattern.