1. Introduction
In this codelab, you'll use gRPC-Python to create a client and server that form the foundation of a route-mapping application written in Python.
By the end of the tutorial, you will have a client that connects to a remote server using gRPC to get the name or postal address of what's located at specific coordinates on a map. A fully fledged application might use this client-server design to enumerate or summarize points of interest along a route.
The service is defined in a Protocol Buffers file, which will be used to generate boilerplate code for the client and server so that they can communicate with each other, saving you time and effort in implementing that functionality.
This generated code takes care of not only the complexities of the communication between the server and client, but also data serialization and deserialization.
What you'll learn
- How to use Protocol Buffers to define a service API.
 - How to build a gRPC-based client and server from a Protocol Buffers definition using automated code generation.
 - An understanding of client-server communication with gRPC.
 
This codelab is aimed at Python developers new to gRPC or seeking a refresher of gRPC, or anyone else interested in building distributed systems. No prior gRPC experience is required.
2. Before you begin
What you'll need
- Python 3.9 or higher. We recommend Python 3.13. For platform-specific installation instructions, see Python Setup and Usage. Alternatively, install a non-system Python using tools like uv or pyenv.
 - pip to install Python packages.
 - venv to create Python virtual environments.
 
The ensurepip and venv packages are part of the Python Standard Library and are typically available by default.
However, some Debian-based distributions (including Ubuntu) choose to exclude them when redistributing python. To install the packages, run:
sudo apt install python3-pip python3-venv
Get the code
To streamline your learning, this codelab offers a pre-built source code scaffold to help you get started. The following steps will guide you through completing the application, including gRPC code generation using the grpc_tools.protoc Protocol Buffer compiler plugin.
grpc-codelabs
The scaffold source code for this codelab is available in the  codelabs/grpc-python-getting-started/start_here directory. If you prefer not to implement the code yourself, the completed source code is available in the  completed directory.
First, create the codelab working directory and cd into it:
mkdir grpc-python-getting-started && cd grpc-python-getting-started
Download and extract the codelab:
curl -sL https://github.com/grpc-ecosystem/grpc-codelabs/archive/refs/heads/v1.tar.gz \
  | tar xvz --strip-components=4 \
  grpc-codelabs-1/codelabs/grpc-python-getting-started/start_here
Alternatively, you can download the .zip file containing only the codelab directory and manually unzip it.
3. Define the service
Your first step is to define the application's gRPC service, its RPC method, and its request and response message types using Protocol Buffers interface definition language. Your service will provide:
- An RPC method called 
GetFeaturethat the server implements and the client calls. - The message types 
PointandFeatureare data structures exchanged between the client and server when using theGetFeaturemethod. The client provides map coordinates as aPointin itsGetFeaturerequest to the server, and the server replies with a correspondingFeaturethat describes whatever is located at those coordinates. 
This RPC method and its message types will all be defined in the protos/route_guide.proto file of the provided source code.
Protocol Buffers are commonly known as protobuf. For more information on gRPC terminology, see gRPC's Core concepts, architecture, and lifecycle.
Message types
In the protos/route_guide.proto file of the source code, first define the Point message type. A Point represents a latitude-longitude coordinate pair on a map. For this codelab, use integers for the coordinates:
message Point {
  int32 latitude = 1;
  int32 longitude = 2;
}
The numbers 1 and 2 are unique ID numbers for each of the fields in the message structure.
Next, define the Feature message type. A Feature uses a string field for the name or postal address of something at a location specified by a Point:
message Feature {
  // The name or address of the feature.
  string name = 1;
  // The point where the feature is located.
  Point location = 2;
}
Service method
The route_guide.proto file has a service structure named RouteGuide that defines one or more methods provided by the application's service.
Add the rpc method GetFeature inside the RouteGuide definition. As explained earlier, this method will look up the name or address of a location from a given set of coordinates, so have GetFeature return a Feature for a given Point:
service RouteGuide {
  // Definition of the service goes here
  // Obtains the feature at a given position.
  rpc GetFeature(Point) returns (Feature) {}
}
This is a unary RPC method: a simple RPC where the client sends a request to the server and waits for a response to come back, just like a local function call.
4. Generate the client and server code
Next, generate the boilerplate gRPC code for both the client and server from the .proto file using the protocol buffer compiler.
For gRPC Python code generation, we created grpcio-tools. It includes:
- The regular  protoc compiler that generates Python code from  
messagedefinitions. - gRPC protobuf plugin that generates Python code (client and server stubs) from the 
servicedefinitions. 
We'll install the grpcio-tools Python package using pip. Let's create a new  python virtual environment (venv) to isolate your project's dependencies from the system packages:
python3 -m venv --upgrade-deps .venv
To activate the virtual environment in bash/zsh shell:
source .venv/bin/activate
For Windows and non-standard shells, see the table at https://docs.python.org/3/library/venv.html#how-venvs-work.
Next, install the grpcio-tools (this also installs the grpcio package):
pip install grpcio-tools
Use the following command to generate the Python boilerplate code:
python -m grpc_tools.protoc --proto_path=./protos  \
 --python_out=. --pyi_out=. --grpc_python_out=. \
 ./protos/route_guide.proto
This will generate the following files for the interfaces we defined in route_guide.proto:
route_guide_pb2.pycontains the code that dynamically creates classes generated from themessagedefinitions.route_guide_pb2.pyiis a "stub file" or "type hint file" generated from themessagedefinitions. It only contains the signatures with no implementation. Stub files can be used by IDEs to provide better autocompletion and error detection.route_guide_pb2_grpc.pyis generated from theservicedefinitions and contains gRPC-specific classes and functions.
gRPC-specific code contains:
RouteGuideStub, which can be used by a gRPC client to invoke RouteGuide RPCs.RouteGuideServicer, which defines the interface for implementations of theRouteGuideservice.add_RouteGuideServicer_to_serverfunction which is used to register aRouteGuideServicerto a gRPC server.
5. Create the service
First let's look at how you create a RouteGuide server. Creating and running a RouteGuide server breaks down into two work items:
- Implementing the servicer interface generated from our service definition with functions that perform the actual "work" of the service.
 - Running a gRPC server at a specific port to listen for requests from clients and transmit responses.
 
You can find the initial RouteGuide server in start_here/route_guide_server.py.
Implement RouteGuide
route_guide_server.py has a RouteGuideServicer class that subclasses the generated class route_guide_pb2_grpc.RouteGuideServicer:
# RouteGuideServicer provides an implementation
# of the methods of the RouteGuide service.
class RouteGuideServicer(route_guide_pb2_grpc.RouteGuideServicer):
RouteGuideServicer implements all the RouteGuide service methods.
Let's look into a simple RPC implementation in detail. Method GetFeature gets a Point from the client and returns the corresponding feature information from its database in Feature.
def GetFeature(self, request, context):
    feature = get_feature(self.db, request)
    if feature is None:
        return route_guide_pb2.Feature(name="", location=request)
    else:
        return feature
The method is passed a route_guide_pb2.Point request for the RPC, and a grpc.ServicerContext object that provides RPC-specific information such as timeout limits. It returns a route_guide_pb2.Feature response.
Starting the server
Once you have implemented all the RouteGuide methods, the next step is to start up a gRPC server so that clients can actually use your service:
def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    route_guide_pb2_grpc.add_RouteGuideServicer_to_server(
        RouteGuideServicer(),
        server,
    )
    listen_addr = "localhost:50051"
    server.add_insecure_port(listen_addr)
    print(f"Starting server on {listen_addr}")
    server.start()
    server.wait_for_termination()
The server start() method is non-blocking. A new thread will be instantiated to handle requests. The thread calling server.start() will often not have any other work to do in the meantime. In this case, you can call server.wait_for_termination() to cleanly block the calling thread until the server terminates.
6. Create the client
In this section, we'll look at creating a client for our RouteGuide service. You can see the initial client code in start_here/route_guide_client.py.
Create a stub
To call service methods, we first need to create a stub.
We instantiate the RouteGuideStub class of the route_guide_pb2_grpc module, generated from our .proto inside of the route_guide_client.py file.
channel = grpc.insecure_channel("localhost:50051")
stub = route_guide_pb2_grpc.RouteGuideStub(channel)
Calling service methods
For RPC methods that return a single response–known as response-unary methods– gRPC Python supports both synchronous (blocking) and asynchronous (non-blocking) control flow semantics.
Simple RPC
First, let's define a Point to call the service with. This should be as simple as instantiating an object from the route_guide_pb2 package with some properties:
point = route_guide_pb2.Point(latitude=412346009, longitude=-744026814)
A synchronous call to the simple RPC GetFeature is nearly as straightforward as calling a local method. The RPC call waits for the server to respond, and will either return a response or raise an exception. We can call the method and see the response like this:
feature = stub.GetFeature(point)
print(feature)
You can inspect the fields of the Feature object and output the result of the request:
if feature.name:
    print(f"Feature called '{feature.name}' at {format_point(feature.location)}")
else:
    print(f"Found no feature at at {format_point(feature.location)}")
7. Try it out
Run the server:
python route_guide_server.py
From a different terminal, activate the virtual environment again, then run the client:
python route_guide_client.py
You'll see output like this, with timestamps omitted for clarity:
name: "16 Old Brook Lane, Warwick, NY 10990, USA"
location {
  latitude: 412346009
  longitude: -744026814
}
Feature called '16 Old Brook Lane, Warwick, NY 10990, USA' at latitude: 412346009, longitude: -744026814
8. What's next
- Learn how gRPC works in Introduction to gRPC and Core concepts
 - Work through the Basics tutorial
 - Explore the Python API reference