Open-source project
graphql-python/gql avatar
graphql-python/gql

GQL chooses your transport for you, and the extras list tells you what you installed

A GraphQL client in Python

1,684 stars197 forksPythonMIT

At a glance

What is it?
A GraphQL client for Python covering HTTP and four WebSocket protocols, sync and async, with local schema validation and a DSL for building queries in code. Suited to Python services talking to a GraphQL endpoint over more than one kind of transport, and heavier than a plain requests call.
Who is it for?
Adopt GQL if your Python service talks to a GraphQL endpoint and you want sync and async execution, subscriptions over WebSockets, or local schema validation before sending a query. Do not adopt it for a single trivial query, since a hand written requests call is smaller than this dependency tree.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 26 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Transports are the reason this library exists

GQL is a GraphQL client for Python, and the README notes that its architecture is inspired by React-Relay and Apollo-Client. That lineage explains the design: a client object, pluggable transports, and an execution model where concurrency is a feature rather than an afterthought.

The transport list is the substance. HTTP is supported, including the multipart protocol for subscriptions. WebSockets is supported through the apollo protocol or the graphql-ws protocol, plus Phoenix channels and the AWS AppSync realtime protocol.

Four WebSocket protocols is more than most clients cover, and each one exists because a specific backend ships it. Choosing GQL over a narrower client is often just this: your endpoint speaks AppSync or Phoenix, and something else does not speak it.

The package declares compatibility with graphene, graphql-core, graphql-js and any other GraphQL implementation following the specification, which is a statement about the server side rather than a guarantee about your particular backend.

Install everything, or install exactly what one transport needs

The README gives one command:

bash
# Quotes may be required on certain shells such as zsh.
pip install "gql[all]"

The comment about quotes is a small practical note, and the note beneath points at the documentation for installing with fewer dependencies depending on the transports you want.

What that all extra actually costs is visible in setup.py. The install_all set is the union of aiohttp, requests, requests_toolbelt, httpx2, websockets, botocore and aiofiles. Two of those are worth pausing on. httpx2 is a dependency most people do not expect to pull in for a GraphQL client, and botocore arrives only because of AppSync, which is an AWS specific transport.

The base install is much smaller: graphql-core, yarl, tenacity and anyio, plus typing_extensions when running below Python 3.11. tenacity in the core dependencies is the retry library, which tells you retry behaviour is part of the default client rather than an extra.

The sync example, and the event loop trap in it

The shortest path to a result is four steps: build a transport with a URL, create a client, write the query with gql(), and execute.

python
from gql import Client, gql
from gql.transport.aiohttp import AIOHTTPTransport

transport = AIOHTTPTransport(url="https://countries.trevorblades.com/")
client = Client(transport=transport)

query = gql(
    """
    query getContinents {
      continents {
        code
        name
      }
    }
"""
)

result = client.execute(query)

The warning attached to this example is the part worth keeping. It will not work if you have an asyncio event loop running, and in some environments such as Jupyter with IPython one is created for you. The documented alternative is the async example.

So the sync API is a convenience for scripts and services, not a general fallback. If your code runs inside an existing async application, use the async form from the start rather than discovering this at the worst moment.

Async execution opens a connection and hands you a session

The async form differs in a way that matters for connection reuse. The client is entered as an async context manager, which starts a connection on the transport and provides a session variable used to execute queries on that connection:

python
async def main():
    transport = AIOHTTPTransport(url="https://countries.trevorblades.com/graphql")
    client = Client(transport=transport)

    query = gql(
        """
        query getContinents {
          continents {
            code
            name
          }
        }
    """
    )

    async with client as session:
        result = await session.execute(query)
        print(result)


asyncio.run(main())

The session is the concurrency unit, and the documentation describes supporting async usage that allows concurrent requests. For a service making many GraphQL calls, that is the difference between one connection per call and a managed one.

Other capabilities listed alongside this: file uploads, custom scalars and enums, batching requests, local request validation using a schema you provide or fetch through an introspection query, and a DSL module for composing queries dynamically rather than writing them as strings.

gql-cli exists for scripts and schema downloads

Beyond the library there is a console entry point declared in setup.py as gql-cli=gql.cli:gql_cli, documented as a script for executing GraphQL queries or downloading schemas from the command line.

The schema download is the interesting part of that. Local validation needs a schema, and a command that fetches one from an endpoint removes the step where you copy a schema file into your repository by hand. That makes validation usable in a first run rather than a second-day activity.

The README's stated target audience for the library is dual, sync or async, which matches the two examples given and matches the way Python services are usually built.

Testing across four HTTP stacks is the maintenance story

The Makefile shows what supporting several transports costs in test infrastructure. There are dedicated targets per transport, each selecting one stack:

bash
pytest tests --aiohttp-only
pytest tests --requests-only
pytest tests --httpx-only
pytest tests --websockets-only

And the default test run is not online:

bash
pytest tests --cov=gql --cov-report=term-missing -vv

while all_tests adds --run-online to include tests that hit real services. That separation is the sensible arrangement for a client library: the default suite is hermetic and fast, and the online suite is opt-in.

The recording of HTTP traffic comes from vcrpy in the test requirements, which is how the hermetic suite covers real request shapes. Coverage is measured with pytest-cov, and the coverage configuration patches subprocess so child processes are counted.

Static analysis and formatting are pinned in the development requirements: black at a target version of py310, isort with the gql package marked first party and a black profile, flake8, and mypy. The mypy configuration is moderately strict, with ignore_missing_imports enabled but check_untyped_defs and disallow_incomplete_defs both on.

Where GQL is the wrong tool

The first wrong case is a single query. If you are making one request in a script, a requests call is a fraction of this dependency tree and has none of the surprises below.

The second is a server. GQL is a client. If you need to serve GraphQL from Python, this is the wrong repository entirely.

The third is the extras sprawl. gql[all] pulls botocore for AppSync and httpx2 for an HTTP stack you may never use. The README points at the lighter installation documentation, and that is the route most projects should take: install the transport you use and leave the rest out.

On maintenance, the last push was on 2026-09-27 and the release is v4.4.0, published the same day, preceded by v4.4.0b1 and v4.4.0b0 betas. The Python floor is 3.10 per the project metadata, and the licence is MIT with a LICENSE file at the root. The documentation is hosted at gql.readthedocs.io, with a Makefile docs target that builds it through Sphinx.

Editorial conclusion

Adopt GQL if your Python service talks to a GraphQL endpoint and you want sync and async execution, subscriptions over WebSockets, or local schema validation before sending a query. Do not adopt it for a single trivial query, since a hand written requests call is smaller than this dependency tree. Verify first which transport extra you actually need, because gql[all] pulls aiohttp, requests, requests_toolbelt, httpx2, websockets, botocore and aiofiles, and the WebSockets extra resolves to a different version range depending on whether Python is 3.11 or newer.

Frequently asked questions

How do I install GQL with every optional dependency?

Run pip install "gql[all]", quoting as needed on shells such as zsh. The README points at the documentation for installing with fewer dependencies if you only need one or two transports.

Which WebSocket protocols does GQL support?

The feature list names the apollo protocol and the graphql-ws protocol, Phoenix channels, and the AWS AppSync realtime protocol, in addition to HTTP and its multipart subscription protocol.

Why does the GQL sync example fail in Jupyter?

The README warns that the basic sync example will not work if an asyncio event loop is running, and that environments such as Jupyter with IPython create one for you. The documented fix is to use the async usage example instead.

Can GQL validate queries before sending them?

Yes. The feature list describes local request validation using a GraphQL schema provided locally or fetched from the backend with an introspection query, and gql-cli can download a schema from the command line.

What does GQL require and what are its base dependencies?

The project metadata sets requires-python to >=3.10. The base install requires graphql-core, yarl, tenacity and anyio, plus typing_extensions below Python 3.11. Installing with all extras adds aiohttp, requests, requests_toolbelt, httpx2, websockets, botocore and aiofiles.

Official sources

  1. graphql-python/gql on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/graphql-python-gql.svg)](https://hysenlabs.com/projects/graphql-python-gql)

Was this useful?