Tutorials: How to build ChatGPT Plugins using ChatGPT

April 09, 2023
Shrikar Archak

Learn how to build ChatGPT plugins

In this blog you will find the step by step guide to building ChatGPT plugins using ChatGPT (sooo meta). Let's get started. We will build a simple plugin to get latest stock information, create and maintain a watchlist.

Prompt for generating the initial skeleton for the ChatGPT Plugin.

Write a simple app using FastAPI with these functionalities. It should use yahoo finance to get the
closing price of a given stock ,add a stock to the watch list, list your watchlist, delete stock
from watch list. Include a `__main__` section which will run this app using uvicorn. the file
where the code will be saved is called main.py

In addition to the normal endpoints, include a route `/.well-known/ai-plugin.json` which server
(as JSON) the contents of `./manifest.json`, located in the same directory as `main.py`.
Exclude this route from the OpenAPI spec, and don’t serve any other static content.
Make sure to add the OpenAPI schema.

With this prompt we can see that ChatGPT does a phenonmenal job of generating the initial skeleton for the plugin. Here are the steps provided by ChatGPT

  1. Install the necessary dependencies:
pip install fastapi uvicorn requests
  1. Install Yahoo Finance library:
pip install yfinance
  1. Create a file called main.py and add the following code:
import json
import os
from fastapi import FastAPI, HTTPException
from fastapi.openapi.utils import get_openapi
from fastapi.responses import JSONResponse
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel
import yfinance as yf

app = FastAPI()
# Mount the static files directory
app.mount("/static", StaticFiles(directory="static"), name="static")
watchlist = set()

class Stock(BaseModel):
 symbol: str

@app.get("/stock/{symbol}", tags=["stocks"])
async def get_stock_price(symbol: str):
 stock = yf.Ticker(symbol)
 historical_data = stock.history(period="1d")

 if historical_data.empty:
     raise HTTPException(status_code=404, detail="Stock not found")

 closing_price = historical_data["Close"].iloc[0]
 return {"symbol": symbol, "closing_price": closing_price}

@app.post("/watchlist", tags=["watchlist"])
async def add_stock_to_watchlist(stock: Stock):
 return {"detail": f"{stock.symbol} added to watchlist"}

@app.get("/watchlist", tags=["watchlist"])
async def get_watchlist():
 return {"watchlist": list(watchlist)}

@app.delete("/watchlist", tags=["watchlist"])
async def delete_stock_from_watchlist(stock: Stock):
 if stock.symbol not in watchlist:
     raise HTTPException(status_code=404, detail="Stock not in watchlist")

 return {"detail": f"{stock.symbol} removed from watchlist"}

@app.get("/.well-known/ai-plugin.json", include_in_schema=False)
async def serve_manifest():
 manifest_file = os.path.join(os.path.dirname(__file__), "manifest.json")
 with open(manifest_file, "r") as f:
     manifest = json.load(f)
 return JSONResponse(content=manifest)

def custom_openapi():
 if app.openapi_schema:
     return app.openapi_schema
 openapi_schema = get_openapi(
     title="Stock Watchlist API",
     description="This is a simple API to get stock prices and manage a watchlist using FastAPI and Yahoo Finance.",
 app.openapi_schema = openapi_schema
 return app.openapi_schema

app.openapi = custom_openapi

if __name__ == "__main__":
 import uvicorn
 uvicorn.run(app, host="", port=8000)

ChatGPT Manifest.json

Here is the sample manifest json taken from the OpenAI website.

    "schema_version": "v1",
    "name_for_human": "StockQnA",
    "name_for_model": "StockQnA",
    "description_for_human": "Your personal assistant for anything StockMarket Related. Manage your watchlist and get stock quotes",
    "description_for_model": "A simple API to get stock prices and manage a watchlist. Call this api for anything stock related and make sure the correct stock symbol is passed to the API",
    "auth": {
      "type": "none"
    "api": {
      "type": "openapi",
      "url": "SERVER_URL_REPLACE_ME/openapi.json",
      "is_user_authenticated": false
    "logo_url": "http://localhost:8000/static/stock-market.png",
    "contact_email": "support@example.com",
    "legal_info_url": "http://www.example.com/legal"

As a followup I also added ChatGPT to serve the logo url. And here is the response

To serve a static file like a logo, you can use FastAPI's StaticFiles class. First, create a directory named static in the same directory as main.py and place the stock-market.png image file inside it. Then, update the main.py file as follows:

from fastapi.staticfiles import StaticFiles
app.mount("/static", StaticFiles(directory="static"), name="static")

At this point in time our code is ready to be deploy and we can test the plugin on ChatGPT. Here is the github repo

Github Repo

  • Deploy the code on by selecting a python env Deploy to CodeSandbox

Deploy Code on Sandbox Add the necessary requirements.txt file with the content below


  • Add main.py from the github repo to the sandbox
  • restart the sandbox to install the requirements
  • Once deployed modify the manifest.json file to add the server url and restart

Deploy to CodeSandbox

ChatGPT plugin install and verification

At this point we should be able to test the plugin in ChatGPT

Deploy to CodeSandbox Deploy to CodeSandbox Deploy to CodeSandbox Deploy to CodeSandbox Deploy to CodeSandbox

What is really interesting is the fact that ChatGPT plugin is able to decide on the number of times the plugin should be called and summarizing the data for us.

Deploy to CodeSandbox

Please let me know if you have any feedback. Thanks to Clusteredbytes for the initial example.

Subscribe to the newsletter

Get notified when new content or topic is released.

You won't receive any spam! ✌️