Eager to get started? This page gives a good introduction to Eve. It assumes that:

  • You already have Eve installed. If you do not, head over to the Installation section.
  • MongoDB is installed.
  • An instance of MongoDB is running.

A Minimal Application

A minimal Eve application looks something like this:

from eve import Eve
app = Eve()

if __name__ == '__main__':

Just save it as run.py. Next, create a new text file with the following content:

DOMAIN = {'people': {}}

Save it as settings.py in the same directory where run.py is stored. This is the Eve configuration file, a standard Python module, and it is telling Eve that your API is comprised of just one accessible resource, people.

Now your are ready to launch your API.

$ python run.py
 * Running on

Now you can consume the API:

$ curl -i
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 82
Server: Eve/0.0.5-dev Werkzeug/0.8.3 Python/2.7.3
Date: Wed, 27 Mar 2013 16:06:44 GMT

Congratulations, your GET request got a nice response back. Let’s look at the payload:

  "_links": {
    "child": [
        "href": "people",
        "title": "people"

API entry points adhere to the HATEOAS principle and provide information about the resources accessible through the API. In our case there’s only one child resource available, that being people.

Try requesting people now:

$ curl
  "_items": [],
  "_links": {
    "self": {
      "href": "people",
      "title": "people"
    "parent": {
      "href": "/",
      "title": "home"

This time we also got an _items list. The _links are relative to the resource being accessed, so you get a link to the parent resource (the home page) and to the resource itself.

By default Eve APIs are read-only:

$ curl -X DELETE
<title>405 Method Not Allowed</title>
<h1>Method Not Allowed</h1>
<p>The method DELETE is not allowed for the requested URL.</p>

Since we didn’t provide any database detail in settings.py, Eve has no clue about the real content of the people collection (it might even be non-existent) and seamlessly serves an empty resource, as we don’t want to let API users down.

Database Interlude

Let’s connect to a database by adding the following lines to settings.py:

# Let's just use the local mongod instance. Edit as needed.

# Please note that MONGO_HOST and MONGO_PORT could very well be left
# out as they already default to a bare bones local 'mongod' instance.
MONGO_HOST = 'localhost'
MONGO_PORT = 27017

# Skip these if your db has no auth. But it really should.
MONGO_USERNAME = '<your username>'
MONGO_PASSWORD = '<your password>'

MONGO_DBNAME = 'apitest'

Due to MongoDB laziness, we don’t really need to create the database collections. Actually we don’t even need to create the database: GET requests on an empty/non-existent DB will be served correctly (200 OK with an empty collection); DELETE/PATCH/PUT will receive appropriate responses (404 Not Found ), and POST requests will create database and collections as needed. However, such an auto-managed database will perform very poorly since it lacks indexes and any sort of optimization.

A More Complex Application

So far our API has been read-only. Let’s enable the full spectrum of CRUD operations:

# Enable reads (GET), inserts (POST) and DELETE for resources/collections
# (if you omit this line, the API will default to ['GET'] and provide
# read-only access to the endpoint).

# Enable reads (GET), edits (PATCH), replacements (PUT) and deletes of
# individual items  (defaults to read-only item access).

RESOURCE_METHODS lists methods allowed at resource endpoints (/people) while ITEM_METHODS lists the methods enabled at item endpoints (/people/<ObjectId>). Both settings have a global scope and will apply to all endpoints. You can then enable or disable HTTP methods at individual endpoint level, as we will soon see.

Since we are enabling editing we also want to enable proper data validation. Let’s define a schema for our people resource.

schema = {
    # Schema definition, based on Cerberus grammar. Check the Cerberus project
    # (https://github.com/pyeve/cerberus) for details.
    'firstname': {
        'type': 'string',
        'minlength': 1,
        'maxlength': 10,
    'lastname': {
        'type': 'string',
        'minlength': 1,
        'maxlength': 15,
        'required': True,
        # talk about hard constraints! For the purpose of the demo
        # 'lastname' is an API entry-point, so we need it to be unique.
        'unique': True,
    # 'role' is a list, and can only contain values from 'allowed'.
    'role': {
        'type': 'list',
        'allowed': ["author", "contributor", "copy"],
    # An embedded 'strongly-typed' dictionary.
    'location': {
        'type': 'dict',
        'schema': {
            'address': {'type': 'string'},
            'city': {'type': 'string'}
    'born': {
        'type': 'datetime',

For more information on validation see Data Validation.

Now let’s say that we want to further customize the people endpoint. We want to:

Here is how the complete people definition looks in our updated settings.py file:

people = {
    # 'title' tag used in item links. Defaults to the resource title minus
    # the final, plural 's' (works fine in most cases but not for 'people')
    'item_title': 'person',

    # by default the standard item entry point is defined as
    # '/people/<ObjectId>'. We leave it untouched, and we also enable an
    # additional read-only entry point. This way consumers can also perform
    # GET requests at '/people/<lastname>'.
    'additional_lookup': {
        'url': 'regex("[\w]+")',
        'field': 'lastname'

    # We choose to override global cache-control directives for this resource.
    'cache_control': 'max-age=10,must-revalidate',
    'cache_expires': 10,

    # most global settings can be overridden at resource level
    'resource_methods': ['GET', 'POST'],

    'schema': schema

Finally we update our domain definition:

    'people': people,

Save settings.py and launch run.py. We can now insert documents at the people endpoint:

$ curl -d '[{"firstname": "barack", "lastname": "obama"}, {"firstname": "mitt", "lastname": "romney"}]' -H 'Content-Type: application/json'
HTTP/1.0 201 OK

We can also update and delete items (but not the whole resource since we disabled that). We can also perform GET requests against the new lastname endpoint:

$ curl -i
HTTP/1.0 200 OK
Etag: 28995829ee85d69c4c18d597a0f68ae606a266cc
Last-Modified: Wed, 21 Nov 2012 16:04:56 GMT
Cache-Control: 'max-age=10,must-revalidate'
Expires: 10
    "firstname": "barack",
    "lastname": "obama",
    "_id": "50acfba938345b0978fccad7"
    "updated": "Wed, 21 Nov 2012 16:04:56 GMT",
    "created": "Wed, 21 Nov 2012 16:04:56 GMT",
    "_links": {
        "self": {"href": "people/50acfba938345b0978fccad7", "title": "person"},
        "parent": {"href": "/", "title": "home"},
        "collection": {"href": "people", "title": "people"}

Cache directives and item title match our new settings. See Features for a complete list of features available and more usage examples.


All examples and code snippets are from the Live demo, which is a fully functional API that you can use to experiment on your own, either on the live instance or locally (you can use the sample client app to populate and/or reset the database).