CLI tool
RDFLib/sparqlwrapper avatar
RDFLib/sparqlwrapper

SPARQLWrapper: running SPARQL queries against a remote endpoint from Python

A wrapper for a remote SPARQL endpoint

570 stars121 forksPythonNOASSERTION

At a glance

What is it?
SPARQLWrapper is a small Python wrapper around a remote SPARQL service that builds the query invocation and converts results into Python objects, with helpers for JSON, XML, CSV and TSV output and for authenticated updates. It suits anyone scripting against a public or internal SPARQL endpoint, and it is a thin client rather than a local RDF store.
Who is it for?
SPARQLWrapper fits a script that needs to query a remote SPARQL endpoint and read the answer in Python, and it does not fit a project that wants to query a local RDF dataset, since the library is only a remote client and rdflib is what you would use for a local store.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 177 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

A remote client, not a local RDF store

SPARQLWrapper is described as a simple Python wrapper around a SPARQL service to remotely execute your queries. It helps by creating the query invocation and, optionally, converting the result into more manageable formats.

Those two jobs are the whole library. There is no local triple store here, no query planner and no reason to bring one in. RDFLib is a separate project, and the dependency list is a single line: rdflib>=6.1.1. That is a useful signal about the intended shape, because rdflib is what does the parsing and the graph handling on the result side, while the wrapper handles the HTTP conversation with the endpoint.

The boundary matters because a lot of confusion in this corner of the RDF world comes from expecting a client to do what a store does. If you have a local RDF file and want to query it, SPARQLWrapper is the wrong tool and rdflib is the right one. If you have a SPARQL endpoint, whether a public knowledge graph or an internal one behind a firewall, this is exactly the abstraction you want.

The library is usable in two ways, as a Python package and as a command line script. The command line script is called rqw, which the readme glosses as spaRQl Wrapper, and running rqw -h shows all of the script's options. So the tool is not only for embedding in a larger program; you can hit an endpoint from a shell without writing a Python file.

The primary language is Python, the default branch is master, and the documentation is hosted at sparqlwrapper.readthedocs.io. The recorded licence is a custom one that GitHub cannot classify, with the terms in LICENSE.txt.

One maintenance fact deserves attention before anything else. The last push was on 2026-04-30, and the recent releases listed are 2.0.0 from 2022-03-13, 1.8.5 from 2019 and 1.8.4 from 2019. So the tree is being worked on while the published versions on PyPI are several years old, and the two are not necessarily the same code.

Installing it, and the three routes that differ in freshness

Three install routes are documented, and each gives you something different.

bash
   $ pip install sparqlwrapper

That is the PyPI route. The GitHub route installs from source.

bash
   $ pip install git+https://github.com/rdflib/sparqlwrapper#egg=sparqlwrapper

And there is a Debian package.

bash
   $ sudo apt-get install python-sparqlwrapper

The readme attaches a warning to the distribution-package route: be aware that there could be a gap between the latest version of SPARQLWrapper and the version available as Debian package. Given the release dates, that gap is years rather than months, so on a Debian machine the packaged version is likely to predate the current source tree by a long way.

The source archive is also available as .zip and .tar.gz formats from the GitHub releases, and the documentation is included in the distribution, so a fully offline install is possible.

The build itself is a legacy hybrid that is still there. pyproject.toml declares a build system of wheel and setuptools with setuptools.build_meta, and the pytest configuration lives in pyproject.toml with coverage enabled for the SPARQLWrapper package, a term-missing report and a durations setting that shows the three slowest tests. There is also a setup.py that does nothing but call setup(), which is the classic pattern for a project that moved its metadata to pyproject.toml without deleting the old entry point.

The type checking configuration is in the same file, and it is strict: mypy runs in strict mode with show_error_codes, pretty output, and warnings for unused configs, unused ignores and unreachable code, scoped to the SPARQLWrapper directory with the test directory excluded. The python_version is set to 3.7, which is the oldest version the configuration targets and a clue about the library's compatibility floor.

The Makefile is a documentation helper. Its doc target delegates to the docs directory with make -C, and its clean target removes the build directory and deletes .pyc files. The variable it exports is the package version, extracted at build time by importing SPARQLWrapper and reading __version__, so the version is defined in one place in the code rather than in a file.

The four query types and what each one hands back

The readme works through the four SPARQL query forms, and the return type changes with each, which is the part that surprises people.

A SELECT query with the JSON return format gives a plain Python dictionary, and the bindings are reached through the results key.

python
    from SPARQLWrapper import SPARQLWrapper, JSON

    sparql = SPARQLWrapper(
        "http://vocabs.ardc.edu.au/repository/api/sparql/"
        "csiro_international-chronostratigraphic-chart_geologic-time-scale-2020"
    )
    sparql.setReturnFormat(JSON)

    # gets the first 3 geological ages
    # from a Geological Timescale database,
    # via a SPARQL endpoint
    sparql.setQuery("""
        PREFIX gts: <http://resource.geosciml.org/ontology/timescale/gts#>

        SELECT *
        WHERE {
            ?a a gts:Age .
        }
        ORDER BY ?a
        LIMIT 3
        """
    )

    try:
        ret = sparql.queryAndConvert()

        for r in ret["results"]["bindings"]:
            print(r)
    except Exception as e:
        print(e)

The API shape is three calls. Construct with the endpoint URL, set the return format, set the query, then queryAndConvert(). The try and except is in the example because a remote endpoint can fail for reasons that have nothing to do with your query, and the pattern of catching broadly and printing the exception is a reminder that this is a network call.

An ASK query gives a boolean, and the example uses the XML format with a different call path. query() returns a response object and .convert() produces the result, and then toxml() serialises it, which is why the readme shows the raw XML of the sparql results document with its boolean element inside.

python
   from SPARQLWrapper import SPARQLWrapper, XML

   sparql = SPARQLWrapper("http://dbpedia.org/sparql")
   sparql.setQuery("""
       ASK WHERE {
           <http://dbpedia.org/resource/Asturias> rdfs:label "Asturias"@es
       }
   """)
   sparql.setReturnFormat(XML)
   results = sparql.query().convert()
   print(results.toxml())

CONSTRUCT and DESCRIBE both produce RDF, so queryAndConvert() returns an rdflib Graph object rather than a dictionary, and you serialise it yourself with the format you want, Turtle in one example and JSON-LD in the other. The Graph return type is the point where the rdflib dependency earns its place.

So the return type is determined by the query form and the format you asked for, and the wrapper does the conversion rather than making you parse the response body. The cost is that you have to know which of the two call shapes applies, queryAndConvert() for the converted object or query() followed by convert() when you want the intermediate response.

Writing to an endpoint, and the two calls you have to make in the right order

Updates are the part of the library that has no working example, and the reason is stated plainly: UPDATE queries write changes to a SPARQL endpoint, so we can't easily show a working example here.

The hypothetical version of it is still worth reading, because it shows the authentication and method API in full.

python
    from SPARQLWrapper import SPARQLWrapper, POST, DIGEST

    sparql = SPARQLWrapper("https://example.org/sparql")
    sparql.setHTTPAuth(DIGEST)
    sparql.setCredentials("some-login", "some-password")
    sparql.setMethod(POST)

Four calls, and the order matters more than it looks. setHTTPAuth selects the authentication scheme, DIGEST being one of the available constants. setCredentials supplies the login and the password for it. setMethod switches the HTTP verb to POST, which is what an update needs, since a SPARQL UPDATE in the body of a GET request is not something endpoints accept.

The authentication choice is worth a moment of thought. DIGEST sends credentials in a challenge-response exchange rather than placing them in the request, which is the reason it exists as a separate constant rather than a flag. The alternative in this library's set of constants is the basic scheme, and there is also a POST constant used here for the method, so the naming overlaps between the two axes and it is easy to confuse setHTTPAuth with setMethod.

What the wrapper does not do is manage the credentials for you. The login and password appear in your source as plain strings in the example, and there is no mention of a credential store, a keyring or an environment variable lookup. If you are running this against a real endpoint, that string is your problem to protect, and the readme says nothing about how to do it.

The other thing worth noting is the asymmetry between reading and writing. A SELECT against a public endpoint needs no setup at all, so the library is useful the moment you install it. An UPDATE needs a credentialed account on a write-enabled endpoint, and most public endpoints, DBPedia included, are read-only. The example endpoint in the readme is named https://example.org/sparql, which is a placeholder rather than a service, and the query it issues is a DELETE against a named graph, which is exactly the operation a shared endpoint would refuse.

Where this sits against rdflib and against a plain HTTP client

Two comparisons make the choice clearer.

The first is against rdflib itself. rdflib is the RDF library proper: it parses and serialises RDF, and it can load a local store or a file and query it. SPARQLWrapper is the layer above it that speaks HTTP to someone else's endpoint. They are not competitors and the dependency list shows the relationship, with rdflib>=6.1.1 as the only runtime requirement. The practical case for SPARQLWrapper is that it handles the part rdflib does not: building the request, sending it, setting the Accept header, and converting the response body into the right Python object.

There is a version of this that is worth considering before taking on the dependency. A SPARQL query is a string, an endpoint is a URL, and the protocol is an HTTP POST with a query parameter and an Accept header. Writing that by hand with requests is a few lines per query, and you get full control over headers, timeouts, proxies and retries, none of which this wrapper's documented surface covers. If your needs are one query in a script, that is often less code than adding a dependency.

The second comparison is against the command line tool. The rqw script is the same functionality without the Python, and it is the better choice for a shell script, for checking an endpoint, or for a one-off from a terminal. It also means you can find out whether an endpoint works before you write any Python.

The limitations are what make the wrapper worth using rather than rolling your own. It knows the format negotiation, it converts to the right object type for the query form, it handles the four return formats, and it has the authentication constants so you are not assembling a digest handshake yourself. Those are the pieces that are tedious to get right and boring to maintain.

Against a full RDF platform, the limitation is scale and features. This library does one thing: send a query and convert the answer. It does not cache, it does not retry a timed-out query, it does not federate across endpoints, and it does not stream large result sets incrementally. A query against a large public endpoint that returns millions of rows will build the whole result in memory, because the wrapper hands you a complete Graph or a complete dictionary. For a small lookup that is fine, and for a bulk extraction you want something else entirely.

Editorial conclusion

SPARQLWrapper fits a script that needs to query a remote SPARQL endpoint and read the answer in Python, and it does not fit a project that wants to query a local RDF dataset, since the library is only a remote client and rdflib is what you would use for a local store. Before depending on it, check the version you install, because the last push was on 2026-04-30 while the newest PyPI release listed is 2.0.0 from 2022, and read the LICENSE.txt file since the recorded licence is a custom one that GitHub cannot classify.

Frequently asked questions

How do I install SPARQLWrapper?

The readme gives three routes: pip install sparqlwrapper from PyPI, pip install git+https://github.com/rdflib/sparqlwrapper#egg=sparqlwrapper from GitHub, and sudo apt-get install python-sparqlwrapper on Debian. The readme warns there could be a gap between the latest version and the Debian package version.

What does SPARQLWrapper return for a SELECT query?

With the JSON return format, queryAndConvert() gives back a Python dictionary, and the rows are reached through ret["results"]["bindings"]. An ASK query gives a boolean, while CONSTRUCT and DESCRIBE queries produce an rdflib Graph object that you serialise yourself.

How do I run an update against a SPARQL endpoint with SPARQLWrapper?

Call setHTTPAuth with a scheme such as DIGEST, then setCredentials with the login and password, then setMethod with POST, then setQuery and run it. The readme notes that no working update example can be shown because updates write changes to the endpoint.

Can I use SPARQLWrapper from the command line?

Yes. Installing the package puts an rqw script in the Python environment, and running rqw -h shows all of its options. The library can be used either as that command line script or as a Python package.

Official sources

  1. Issues
  2. Project website
  3. RDFLib/sparqlwrapper on GitHub
  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/rdflib-sparqlwrapper.svg)](https://hysenlabs.com/projects/rdflib-sparqlwrapper)

Was this useful?