W3docs

MongoDB Insert

Learn how to insert single and multiple documents into MongoDB using Python's pymongo driver — with error handling, ObjectId, and bulk insert options.

MongoDB stores data as documents — flexible, JSON-like objects that can hold nested fields and arrays. This chapter shows you how to insert one document at a time with insert_one(), insert many documents in a single call with insert_many(), understand the auto-generated _id field, handle duplicate-key errors, and choose between ordered and unordered bulk inserts.

Prerequisites

pip install pymongo

Connecting to MongoDB

Import MongoClient and open a connection before performing any insert operations. When MongoDB is running locally with default settings, you can call MongoClient() with no arguments:

from pymongo import MongoClient

client = MongoClient()          # connects to localhost:27017
db = client["bookstore"]        # database (created on first write)
books = db["books"]             # collection (created on first write)

To connect to a remote server or Atlas, pass a connection URI:

client = MongoClient("mongodb://username:password@hostname:27017/")

MongoClient maintains a connection pool internally, so you should create one client per application and reuse it across all operations.

Inserting a Single Document with insert_one()

insert_one() adds one document to a collection and returns an InsertOneResult object. The most useful property on that object is inserted_id, which holds the _id that was assigned to the new document.

from pymongo import MongoClient

client = MongoClient()
books = client["bookstore"]["books"]

document = {
    "title": "The Pragmatic Programmer",
    "author": "David Thomas",
    "year": 1999,
    "in_stock": True,
}

result = books.insert_one(document)
print("Inserted _id:", result.inserted_id)

Example output:

Inserted _id: 64b3e2c1f0a1234567890abc

The exact _id value will differ each time — MongoDB generates a unique ObjectId unless you supply your own _id.

Understanding the _id Field

Every MongoDB document must have an _id field. If you do not include one, the driver automatically generates a bson.ObjectId value. ObjectId is a 12-byte value that encodes:

  • a 4-byte Unix timestamp (seconds),
  • a 5-byte random value unique to the machine and process,
  • a 3-byte incrementing counter.

This means ObjectId values are roughly chronologically ordered and globally unique without any coordination between servers.

You can supply your own _id if you have a natural unique key (for example, an ISBN):

result = books.insert_one({
    "_id": "978-0-13-468599-1",
    "title": "The Pragmatic Programmer",
    "author": "David Thomas",
    "year": 1999,
})
print("Inserted _id:", result.inserted_id)
# Inserted _id: 978-0-13-468599-1

If you insert a second document with the same _id, MongoDB raises a DuplicateKeyError (see Handling Errors below).

Inserting Multiple Documents with insert_many()

insert_many() accepts a list of documents and inserts all of them in a single network round-trip. It returns an InsertManyResult whose inserted_ids attribute holds the list of assigned _id values in insertion order.

from pymongo import MongoClient

client = MongoClient()
books = client["bookstore"]["books"]

new_books = [
    {"title": "Clean Code", "author": "Robert C. Martin", "year": 2008},
    {"title": "Refactoring",  "author": "Martin Fowler",    "year": 1999},
    {"title": "Design Patterns", "author": "Gang of Four",  "year": 1994},
]

result = books.insert_many(new_books)
print("Inserted IDs:", result.inserted_ids)

Example output:

Inserted IDs: [ObjectId('...'), ObjectId('...'), ObjectId('...')]

Ordered vs. Unordered Inserts

By default, insert_many() uses ordered mode: documents are inserted one by one in list order, and processing stops at the first error.

Pass ordered=False for unordered mode: MongoDB attempts every document independently and collects all errors before raising an exception. This is faster for large batches where you expect some duplicates and want to skip bad documents rather than abort the whole batch.

from pymongo import MongoClient
from pymongo.errors import BulkWriteError

client = MongoClient()
books = client["bookstore"]["books"]

# Two documents with duplicate _id values mixed in
docs = [
    {"_id": 1, "title": "Book A"},
    {"_id": 2, "title": "Book B"},
    {"_id": 1, "title": "Duplicate — will fail"},  # duplicate _id
    {"_id": 3, "title": "Book C"},
]

try:
    result = books.insert_many(docs, ordered=False)
    print("Inserted:", result.inserted_ids)
except BulkWriteError as e:
    # inserted_ids still shows the documents that succeeded
    print("Some inserts failed:", e.details["nInserted"], "succeeded")
    for err in e.details["writeErrors"]:
        print("  Error on index", err["index"], "—", err["errmsg"])

With ordered=False, Book A, Book B, and Book C are inserted even though the duplicate fails. With ordered=True (the default), processing would stop at the third document and Book C would never be inserted.

Handling Errors

Duplicate Key Error

Inserting a document whose _id (or any field covered by a unique index) already exists raises pymongo.errors.DuplicateKeyError:

from pymongo import MongoClient
from pymongo.errors import DuplicateKeyError

client = MongoClient()
books = client["bookstore"]["books"]

try:
    books.insert_one({"_id": "isbn-001", "title": "First"})
    books.insert_one({"_id": "isbn-001", "title": "Duplicate"})  # raises
except DuplicateKeyError as e:
    print("Duplicate key:", e.details["keyValue"])

Output:

Duplicate key: {'_id': 'isbn-001'}

Connection Errors

MongoClient() succeeds even when MongoDB is not running — the error surfaces only when you make a real request. Wrap insert operations in a try/except to handle connection failures gracefully:

from pymongo import MongoClient
from pymongo.errors import ConnectionFailure, PyMongoError

client = MongoClient(serverSelectionTimeoutMS=3000)

try:
    result = books.insert_one({"title": "Test"})
    print("Inserted:", result.inserted_id)
except ConnectionFailure:
    print("Could not reach MongoDB server.")
except PyMongoError as e:
    print("MongoDB error:", e)

Checking the Result

Both insert_one() and insert_many() return result objects with useful properties:

Result objectKey properties
InsertOneResultinserted_id, acknowledged
InsertManyResultinserted_ids (list), acknowledged

acknowledged is True when MongoDB confirmed the write. It can be False only when you use an unacknowledged write concern (w=0), which skips the confirmation for maximum speed at the cost of not knowing whether the write succeeded.

Full Working Example

The following self-contained script connects to a local MongoDB server, inserts several documents, and prints the results:

from pymongo import MongoClient
from pymongo.errors import DuplicateKeyError, BulkWriteError

DB_NAME = "demo_bookstore"
COL_NAME = "books"

def main():
    client = MongoClient(serverSelectionTimeoutMS=3000)

    # Verify connectivity
    client.admin.command("ping")
    print("Connected to MongoDB")

    col = client[DB_NAME][COL_NAME]
    col.drop()  # start fresh for this demo

    # --- insert_one ---
    result = col.insert_one({
        "_id": "isbn-001",
        "title": "The Pragmatic Programmer",
        "author": "David Thomas",
        "year": 1999,
    })
    print("insert_one _id:", result.inserted_id)

    # --- insert_many ---
    result = col.insert_many([
        {"title": "Clean Code",      "author": "Robert C. Martin", "year": 2008},
        {"title": "Refactoring",     "author": "Martin Fowler",    "year": 1999},
        {"title": "Design Patterns", "author": "Gang of Four",     "year": 1994},
    ])
    print("insert_many IDs:", result.inserted_ids)

    # --- duplicate key ---
    try:
        col.insert_one({"_id": "isbn-001", "title": "Duplicate"})
    except DuplicateKeyError:
        print("Caught DuplicateKeyError as expected")

    # --- unordered bulk insert ---
    docs = [
        {"_id": "isbn-002", "title": "Book A"},
        {"_id": "isbn-001", "title": "Dup — will fail"},  # duplicate
        {"_id": "isbn-003", "title": "Book C"},
    ]
    try:
        col.insert_many(docs, ordered=False)
    except BulkWriteError as e:
        print("Bulk insert: succeeded =", e.details["nInserted"],
              ", failed =", len(e.details["writeErrors"]))

    print("Total documents:", col.count_documents({}))

    # Clean up
    client.drop_database(DB_NAME)

if __name__ == "__main__":
    main()

Expected output:

Connected to MongoDB
insert_one _id: isbn-001
insert_many IDs: [ObjectId('...'), ObjectId('...'), ObjectId('...')]
Caught DuplicateKeyError as expected
Bulk insert: succeeded = 2 , failed = 1
Total documents: 6

Common Gotchas

PyMongo mutates your document

When you pass a plain dict to insert_one(), PyMongo adds an _id key to the original dict:

doc = {"title": "My Book"}
col.insert_one(doc)
print(doc)  # {'title': 'My Book', '_id': ObjectId('...')}

If you plan to reuse the same dict (for example in a loop), pass a copy instead: col.insert_one(doc.copy()).

Large inserts: use insert_many() not a loop

Inserting 10,000 documents one by one makes 10,000 network round-trips. Use insert_many() to send them all at once — it is orders of magnitude faster for bulk loads.

If your list is very large (millions of documents), split it into batches of a few thousand to avoid exceeding the 48 MB BSON document-size limit per batch.

Datetime fields need datetime objects, not strings

MongoDB stores dates as BSON Date (milliseconds since epoch). Use Python's datetime.datetime for date fields so they are stored and queried correctly:

from datetime import datetime
col.insert_one({"title": "New Book", "published": datetime(2024, 3, 15)})

Next Steps

Was this page helpful?