Version: 1.0.0
Base URL: http://127.0.0.1:8000 (local) or your deployed URL
The MarketWay Navigator API provides endpoints for:
- Intelligent Q&A: Ask questions about the market, products, and history
- Product Search: Find which market lines sell specific products
- Line Information: Get detailed information about specific market lines
- Voice Interaction: Speech-to-text and text-to-speech capabilities
- Image Recognition: Identify products from images
- Navigation: Get directions to specific market lines
- Market History: Access historical information about Bamenda Main Market
Currently, the API does not require authentication for most endpoints. However, ensure you have the proper CORS configuration if calling from a web browser.
http://127.0.0.1:8000
Content-Type: application/json
Accept: application/jsonFor file uploads (voice/image endpoints):
Content-Type: multipart/form-dataAsk general questions about the market, products, or history. The API intelligently searches local data first, then falls back to online search if needed.
URL: /ask
Method: POST
Content-Type: application/json
Body:
{
"question": "Where can I find medicine?"
}| Field | Type | Required | Description |
|---|---|---|---|
question |
string | Yes | The question to ask about the market |
Status: 200 OK
Content-Type: application/json
{
"answer": "Found 'medicine' in the following lines: Mothers Line, Obama Line, Peaceful Line.",
"source": "local",
"images": [
"/images/Mothers Line.jpg",
"/images/Obama Line.jpg",
"/images/Peaceful Line.jpg"
]
}| Field | Type | Description |
|---|---|---|
answer |
string | The answer to the question |
source |
string | Data source: "local", "online", or "combined" |
images |
array | Array of image URLs related to the answer |
// Find products
{
"question": "Where can I buy shoes?"
}
// Market history
{
"question": "When was the market built?"
}
// General queries
{
"question": "What can I find in Victory Line?"
}Search for products and find which market lines sell them.
URL: /product/search
Method: GET
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
q |
string | Yes | Product name to search for |
Example:
GET /product/search?q=shoes
Status: 200 OK
Content-Type: application/json
{
"query": "shoes",
"results": [
{
"line_name": "Blessed Line",
"items_sold": ["shoes", "boxes", "buckets", "kitchenutensils"],
"layout": {
"column": "right",
"order": 4
}
},
{
"line_name": "Victory Line",
"items_sold": ["beads", "jewelries", "clothes", "babystuff", "shoes", "wigs", "baggyjeans", "bodylotion", "schoolequipment"],
"layout": {
"column": "right",
"order": 5
}
},
{
"line_name": "Wisdom Line",
"items_sold": ["clothes", "bags", "shoes", "sleepers", "kitchenutensils"],
"layout": {
"column": "right",
"order": 8
}
},
{
"line_name": "Godly Line",
"items_sold": ["shoes", "dresses", "roomdecoitems", "bags", "babystuff"],
"layout": {
"column": "right",
"order": 9
}
}
]
}| Field | Type | Description |
|---|---|---|
query |
string | The search query that was executed |
results |
array | Array of line objects that sell the searched product |
results[].line_name |
string | Name of the market line |
results[].items_sold |
array | List of items sold in this line |
results[].layout |
object | Layout information for the line |
results[].layout.column |
string | Column position: "left" or "right" |
results[].layout.order |
number | Order/position within the column |
Get detailed information about a specific market line.
URL: /line/info/{line_name}
Method: GET
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
line_name |
string | Yes | Name of the market line (case-insensitive) |
Example:
GET /line/info/Victory Line
Status: 200 OK
Content-Type: application/json
{
"line_name": "Victory Line",
"items_sold": [
"beads",
"jewelries",
"clothes",
"babystuff",
"shoes",
"wigs",
"baggyjeans",
"bodylotion",
"schoolequipment"
],
"layout": {
"column": "right",
"order": 5
},
"image_url": "/images/Victory line.jpg"
}| Field | Type | Description |
|---|---|---|
line_name |
string | Name of the market line |
items_sold |
array | List of items sold in this line |
layout |
object | Layout information |
layout.column |
string | Column position: "left" or "right" |
layout.order |
number | Order/position within the column |
image_url |
string | null | URL to the line's image, or null if not available |
Status: 404 Not Found
{
"detail": "Line not found"
}Get historical information about Bamenda Main Market.
URL: /history
Method: GET
Example:
GET /history
Status: 200 OK
Content-Type: application/json
{
"history": "Bamenda Main Market, also known as Bamenda Central Market, is one of the largest and most vibrant markets in the North West Region of Cameroon..."
}| Field | Type | Description |
|---|---|---|
history |
string | Full historical text about the market (extracted from PDF) |
Submit a voice query and receive an audio response.
URL: /voice/query
Method: POST
Content-Type: multipart/form-data
Form Data:
| Field | Type | Required | Description |
|---|---|---|---|
file |
file | Yes | Audio file (WAV, MP3, etc.) containing the voice query |
Example (using FormData):
const formData = new FormData();
formData.append('file', audioBlob, 'query.wav');
fetch('/voice/query', {
method: 'POST',
body: formData
});Status: 200 OK
Content-Type: audio/mpeg
Returns an MP3 audio file containing the spoken response.
- Speech-to-Text: Your audio is converted to text
- Query Processing: The text is processed like a regular search
- Text-to-Speech: The response is converted back to audio
- Audio Response: MP3 file is returned
Status: 400 Bad Request
{
"detail": "Could not understand audio"
}Identify products from an uploaded image.
URL: /image/identify
Method: POST
Content-Type: multipart/form-data
Form Data:
| Field | Type | Required | Description |
|---|---|---|---|
file |
file | Yes | Image file (JPG, PNG, etc.) containing the product |
Example (using FormData):
const formData = new FormData();
formData.append('file', imageFile, 'product.jpg');
fetch('/image/identify', {
method: 'POST',
body: formData
});Status: 200 OK
Content-Type: application/json
{
"identified_product": "shoes",
"confidence": 0.85,
"matching_lines": [
{
"line_name": "Blessed Line",
"items_sold": ["shoes", "boxes", "buckets", "kitchenutensils"]
},
{
"line_name": "Victory Line",
"items_sold": ["beads", "jewelries", "clothes", "babystuff", "shoes", "wigs", "baggyjeans", "bodylotion", "schoolequipment"]
}
]
}Note: The exact response structure depends on the image service implementation. Check with your backend team for the specific fields returned.
Get directions to a specific market line.
URL: /navigate
Method: GET
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
line_name |
string | Yes | Target line name to navigate to |
Example:
GET /navigate?line_name=Victory Line
Status: 200 OK
Content-Type: application/json
{
"line_name": "Victory Line",
"directions": "Go to the RIGHT column, it's the 5th line on that side.",
"layout": {
"column": "right",
"order": 5
}
}| Field | Type | Description |
|---|---|---|
line_name |
string | Name of the target line |
directions |
string | Human-readable directions to the line |
layout |
object | Layout information |
layout.column |
string | Column position: "left" or "right" |
layout.order |
number | Order/position within the column |
Status: 404 Not Found
{
"detail": "Line not found"
}interface Line {
line_name: string;
items_sold: string[];
layout: {
column: "left" | "right";
order: number;
};
}interface Layout {
column: "left" | "right"; // Which column of the market
order: number; // Position within that column (1-based)
}The market has the following lines:
Left Column:
- Mothers Line
- Family Line
- Magazine Line
- Onitsha Line
- Best Line
Right Column:
- Fish Line
- Obama Line
- Peaceful Line
- Blessed Line
- Victory Line
- Universal Line
- Fashion Line
- Wisdom Line
- Godly Line
- Rapa Line
{
"detail": "Error message description"
}| Status Code | Meaning | Common Causes |
|---|---|---|
200 |
Success | Request completed successfully |
400 |
Bad Request | Invalid input, missing required fields |
404 |
Not Found | Line not found, resource doesn't exist |
422 |
Unprocessable Entity | Validation error (check request format) |
500 |
Internal Server Error | Server-side error |
try {
const response = await fetch('/product/search?q=shoes');
if (!response.ok) {
const error = await response.json();
console.error('API Error:', error.detail);
// Handle error appropriately
return;
}
const data = await response.json();
// Process successful response
} catch (error) {
console.error('Network Error:', error);
// Handle network errors
}async function askQuestion(question) {
const response = await fetch('http://127.0.0.1:8000/ask', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ question })
});
const data = await response.json();
console.log('Answer:', data.answer);
console.log('Images:', data.images);
return data;
}
// Usage
askQuestion('Where can I buy clothes?');async function searchProduct(productName) {
const response = await fetch(
`http://127.0.0.1:8000/product/search?q=${encodeURIComponent(productName)}`
);
const data = await response.json();
console.log(`Found ${data.results.length} lines selling ${productName}`);
return data.results;
}
// Usage
searchProduct('shoes');async function getLineInfo(lineName) {
const response = await fetch(
`http://127.0.0.1:8000/line/info/${encodeURIComponent(lineName)}`
);
if (!response.ok) {
throw new Error('Line not found');
}
const data = await response.json();
return data;
}
// Usage
getLineInfo('Victory Line');async function sendVoiceQuery(audioBlob) {
const formData = new FormData();
formData.append('file', audioBlob, 'query.wav');
const response = await fetch('http://127.0.0.1:8000/voice/query', {
method: 'POST',
body: formData
});
// Response is an audio file
const audioBlob = await response.blob();
const audioUrl = URL.createObjectURL(audioBlob);
// Play the audio
const audio = new Audio(audioUrl);
audio.play();
return audioUrl;
}async function identifyProduct(imageFile) {
const formData = new FormData();
formData.append('file', imageFile);
const response = await fetch('http://127.0.0.1:8000/image/identify', {
method: 'POST',
body: formData
});
const data = await response.json();
return data;
}
// Usage with file input
document.getElementById('imageInput').addEventListener('change', async (e) => {
const file = e.target.files[0];
const result = await identifyProduct(file);
console.log('Identified:', result);
});async function getDirections(lineName) {
const response = await fetch(
`http://127.0.0.1:8000/navigate?line_name=${encodeURIComponent(lineName)}`
);
if (!response.ok) {
throw new Error('Line not found');
}
const data = await response.json();
console.log('Directions:', data.directions);
return data;
}
// Usage
getDirections('Victory Line');import { useState } from 'react';
interface AskResponse {
answer: string;
source: string;
images: string[];
}
function MarketSearch() {
const [question, setQuestion] = useState('');
const [response, setResponse] = useState<AskResponse | null>(null);
const [loading, setLoading] = useState(false);
const handleSearch = async () => {
setLoading(true);
try {
const res = await fetch('http://127.0.0.1:8000/ask', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ question })
});
const data = await res.json();
setResponse(data);
} catch (error) {
console.error('Search failed:', error);
} finally {
setLoading(false);
}
};
return (
<div>
<input
type="text"
value={question}
onChange={(e) => setQuestion(e.target.value)}
placeholder="Ask about the market..."
/>
<button onClick={handleSearch} disabled={loading}>
{loading ? 'Searching...' : 'Search'}
</button>
{response && (
<div>
<p>{response.answer}</p>
<div>
{response.images.map((img, i) => (
<img key={i} src={img} alt="Market line" />
))}
</div>
</div>
)}
</div>
);
}import axios from 'axios';
const api = axios.create({
baseURL: 'http://127.0.0.1:8000',
headers: {
'Content-Type': 'application/json'
}
});
// Ask a question
async function askQuestion(question) {
const { data } = await api.post('/ask', { question });
return data;
}
// Search products
async function searchProduct(query) {
const { data } = await api.get('/product/search', {
params: { q: query }
});
return data;
}
// Get line info
async function getLineInfo(lineName) {
const { data } = await api.get(`/line/info/${lineName}`);
return data;
}
// Navigate
async function navigate(lineName) {
const { data } = await api.get('/navigate', {
params: { line_name: lineName }
});
return data;
}For interactive testing and exploration, visit the auto-generated Swagger UI documentation:
http://127.0.0.1:8000/docs
This provides:
- Interactive API testing
- Request/response examples
- Schema definitions
- Try-it-out functionality
If you're calling the API from a web browser, ensure CORS is properly configured on the backend. The backend should include appropriate CORS headers:
# Backend CORS configuration (already handled in main.py)
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # Configure appropriately for production
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)Currently, there are no rate limits on the API. However, be mindful of:
- External API usage (Tavily search has its own limits)
- File upload sizes for voice/image endpoints
- Server resources for voice/image processing
Always implement proper error handling for network failures and API errors.
Show loading indicators during API calls for better UX.
For search inputs, debounce user input to avoid excessive API calls.
import { debounce } from 'lodash';
const debouncedSearch = debounce(async (query) => {
const results = await searchProduct(query);
// Update UI with results
}, 300);Cache frequently accessed data (like market history or line information) to reduce API calls.
Image URLs are relative paths. Prepend the base URL when displaying:
const imageUrl = `http://127.0.0.1:8000${data.image_url}`;Always encode URL parameters, especially line names with spaces:
const lineName = encodeURIComponent('Victory Line');
fetch(`/line/info/${lineName}`);For issues, questions, or feature requests:
- Check the interactive docs at
/docs - Review the backend README.md
- Contact the backend development team
Last Updated: 2025-11-28
API Version: 1.0.0