A powerful Speech-to-Text API built with Django REST Framework and AssemblyAI. Textor-AI provides enterprise-grade transcription capabilities with advanced features like multi-language support, real-time status tracking, and comprehensive transcription management.
- 🎯 High-accuracy speech-to-text conversion
- 🌍 Multi-language transcription support
- 📊 Real-time transcription status tracking
- 🔄 Automatic synchronization with AssemblyAI
- 🔐 Secure token-based authentication
- 📱 RESTful API with comprehensive documentation
- 📂 Support for multiple audio formats
- 📋 Grouped transcription listing with pagination
- ⚡ Rate limiting and error handling
- Clone the repository:
git clone https://github.com/dehyabi/textor-ai.git
cd textor-ai
- Create and activate a virtual environment:
python3 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
- Install dependencies:
pip install -r requirements.txt
- Create a
.env
file in the project root and add your AssemblyAI API key:
ASSEMBLYAI_API_KEY=your_api_key_here
- Run migrations:
python3 manage.py migrate
- Create a superuser:
python3 manage.py createsuperuser
- Run the server:
python3 manage.py runserver
- Heroku CLI installed
- Git repository initialized
- AssemblyAI API key
- PostgreSQL installed locally (for testing)
Before deploying to Heroku, test your deployment locally:
- Install all requirements:
pip install -r requirements.txt
- Create a .env file with required variables:
DJANGO_SECRET_KEY=your-secret-key
DJANGO_DEBUG=True
ASSEMBLYAI_API_KEY=your-assemblyai-api-key
DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1
- Test with gunicorn locally:
gunicorn speech_to_text_api.wsgi:application
- Install the Heroku CLI and login:
curl https://cli-assets.heroku.com/install.sh | sh
heroku login
- Create a new Heroku app:
heroku create textor-ai
- Add PostgreSQL addon:
heroku addons:create heroku-postgresql:mini
- Configure environment variables:
# Generate a secure secret key
python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"
# Set environment variables
heroku config:set DJANGO_SECRET_KEY=<generated-secret-key>
heroku config:set DJANGO_DEBUG=False
heroku config:set ASSEMBLYAI_API_KEY=your-assemblyai-api-key
heroku config:set DJANGO_ALLOWED_HOSTS=your-app-name.herokuapp.com
heroku config:set CORS_ALLOWED_ORIGINS=https://your-frontend-domain.com
- Deploy to Heroku:
# Add Heroku remote
heroku git:remote -a your-app-name
# Push to Heroku
git push heroku main
- Run migrations and create superuser:
heroku run python manage.py migrate
heroku run python manage.py createsuperuser
- Verify deployment:
# Open the app in browser
heroku open
# Check logs for any errors
heroku logs --tail
-
Fork this repository to your GitHub account.
-
Visit Render.com and create an account if you haven't already.
-
Click the "New +" button and select "Blueprint" from the dropdown.
-
Connect your GitHub account and select your forked repository.
-
Render will automatically detect the
render.yaml
configuration and set up your services. -
Set your AssemblyAI API key in the environment variables:
- Go to your web service settings
- Click on "Environment"
- Add
ASSEMBLYAI_API_KEY
with your API key
-
Your app will be deployed automatically. The URL will be:
https://textor-ai.onrender.com
-
Visit Render.com and create an account.
-
Click the "New +" button and select "Web Service".
-
Connect your GitHub repository.
-
Fill in the following settings:
- Name: textor-ai
- Environment: Python
- Region: Oregon (or your preferred region)
- Branch: main
- Build Command:
pip install -r requirements.txt
- Start Command:
gunicorn speech_to_text_api.wsgi:application --bind 0.0.0.0:$PORT --workers 4 --threads 2
-
Add the following environment variables:
PYTHON_VERSION=3.10.12 DJANGO_DEBUG=False DJANGO_ALLOWED_HOSTS=.onrender.com DJANGO_SETTINGS_MODULE=speech_to_text_api.settings ASSEMBLYAI_API_KEY=your-api-key
-
Create a PostgreSQL database:
- Click "New +" and select "PostgreSQL"
- Choose the "Starter" plan
- Note the internal database URL
-
Add the database URL to your web service:
- Go back to your web service settings
- Add
DATABASE_URL
with the internal database URL
-
Run migrations:
# Using Render Shell python manage.py migrate
-
Create a superuser:
# Using Render Shell python manage.py createsuperuser
-
View logs:
- Go to your web service dashboard
- Click on "Logs" in the left sidebar
-
Monitor metrics:
- Click on "Metrics" in the left sidebar
- View CPU, Memory, and Network usage
-
If static files are not serving:
# Using Render Shell python manage.py collectstatic --noinput
-
If you need to restart the service:
- Go to your web service dashboard
- Click "Manual Deploy" > "Deploy latest commit"
-
Check application logs for errors:
- Go to your web service dashboard
- Click on "Logs"
- Select "All" to view all log types
Textor-AI supports both traditional and containerized deployment on Heroku.
Follow the standard Heroku deployment steps mentioned above.
- Install the Heroku CLI and login:
curl https://cli-assets.heroku.com/install.sh | sh
heroku login
heroku container:login
- Create a new Heroku app:
heroku create textor-ai
- Set stack to container:
heroku stack:set container
- Configure environment variables:
heroku config:set DJANGO_SECRET_KEY=$(python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())")
heroku config:set DJANGO_DEBUG=False
heroku config:set ASSEMBLYAI_API_KEY=your-assemblyai-api-key
heroku config:set DJANGO_ALLOWED_HOSTS=your-app-name.herokuapp.com
- Push and release the container:
# Build and push the container
heroku container:push web
# Release the container
heroku container:release web
- Run migrations:
heroku run python manage.py migrate
For local development with Docker:
- Build the container:
docker build -t textor-ai .
- Run the container:
docker run -p 8000:8000 \
-e PORT=8000 \
-e DJANGO_SECRET_KEY=your-secret-key \
-e DJANGO_DEBUG=True \
-e ASSEMBLYAI_API_KEY=your-api-key \
-e DJANGO_ALLOWED_HOSTS=localhost \
textor-ai
- Run tests in container:
docker run textor-ai python manage.py test
The container is configured with:
- Python 3.10.12 slim base image
- Non-root user for security
- Optimized gunicorn settings
- Static file collection
- Health checks
- Environment variable configuration
Key container features:
- Multi-stage builds for smaller images
- Security best practices
- Performance optimizations
- Health monitoring
- Automatic migrations
- Static file handling
- Scale horizontally:
heroku ps:scale web=3
- Scale vertically:
heroku ps:resize web=standard-2x
- Enable auto-scaling:
heroku addons:create adept-scale:basic
- View container metrics:
# View container status
heroku container:status
# View container logs
heroku logs --tail
# View process metrics
heroku ps:metrics
- Container health checks:
# Check container health
curl https://your-app-name.herokuapp.com/health/
# View detailed metrics
heroku ps:metrics --json
- A PythonAnywhere account (Sign up here)
- Git repository with your code
- AssemblyAI API key
-
Log in to PythonAnywhere and open a Bash console.
-
Clone your repository:
git clone https://github.com/yourusername/textor-ai.git
cd textor-ai
- Create and activate a virtual environment:
mkvirtualenv --python=/usr/bin/python3.10 textor-ai
workon textor-ai
- Install dependencies:
pip install -r requirements.txt
-
Create a new web app:
- Go to the Web tab
- Click "Add a new web app"
- Choose "Manual configuration"
- Select Python 3.10
- Note your domain name (e.g., yourusername.pythonanywhere.com)
-
Configure the virtual environment:
- In the Web tab, under "Virtualenv:"
- Enter:
/home/yourusername/.virtualenvs/textor-ai
-
Configure WSGI file:
- Click on the WSGI configuration file link
- Delete everything
- Copy content from
pythonanywhere_wsgi.py
- Update
path
variable with your username - Save the file
-
Set up static files:
- In the Web tab, add:
URL: /static/ Directory: /home/yourusername/textor-ai/staticfiles
- In the Web tab, add:
-
Create and configure environment variables:
- Go to the Web tab
- Under "Environment variables", add:
DJANGO_SECRET_KEY=your-secret-key DJANGO_DEBUG=False DJANGO_ALLOWED_HOSTS=yourusername.pythonanywhere.com ASSEMBLYAI_API_KEY=your-assemblyai-api-key
-
Set up the database:
# In PythonAnywhere console python manage.py migrate python manage.py createsuperuser
-
Collect static files:
python manage.py collectstatic --noinput
-
Configure CORS:
- Add your domain to
ALLOWED_HOSTS
in settings.py - Add your frontend domain to
CORS_ALLOWED_ORIGINS
if needed
- Add your domain to
-
Reload the web app:
- Go to the Web tab
- Click the "Reload" button
Make sure files have correct permissions:
chmod 755 /home/yourusername/textor-ai
chmod 755 /home/yourusername/textor-ai/be
chmod 644 /home/yourusername/textor-ai/be/db.sqlite3
PythonAnywhere provides MySQL by default. To use it:
- Go to the Databases tab
- Initialize MySQL and set a password
- Create a new database:
CREATE DATABASE textor_ai CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
- Update your environment variables with the database URL:
DATABASE_URL=mysql://yourusername:[email protected]/textor_ai
Set up database backups:
- Go to the Tasks tab
- Add a daily task:
mysqldump -u yourusername -p'password' textor_ai > /home/yourusername/backups/textor_ai_$(date +%Y%m%d).sql
For SSL (PythonAnywhere Business accounts):
- Go to the Web tab
- Enable "Force HTTPS"
- Update your ALLOWED_HOSTS and CORS settings accordingly
-
Check error logs:
- Go to the Web tab
- Click "Error log" link
-
Check access logs:
- Go to the Web tab
- Click "Access log" link
-
Common issues:
- Static files not loading: Check paths in Web tab
- 502 Bad Gateway: Check error logs and WSGI file
- Database errors: Check database URL and migrations
- Import errors: Check virtual environment and requirements
-
Reload after changes:
- Always click "Reload" in Web tab after making changes
- Some changes require server restart
- Pull latest changes:
cd ~/textor-ai
git pull origin main
- Update dependencies:
workon textor-ai
pip install -r requirements.txt
- Run migrations:
python manage.py migrate
- Collect static files:
python manage.py collectstatic --noinput
- Reload the web app in the Web tab
- Generate new Django secret key
- Set DEBUG to False
- Configure allowed hosts
- Set up CORS properly
- Configure SSL/HTTPS
- Set up proper database backup
- Configure rate limiting
- Set up monitoring
- Configure error logging
- Set up Sentry for error tracking:
heroku config:set SENTRY_DSN=your-sentry-dsn
- Enable application monitoring:
# Add New Relic
heroku addons:create newrelic:wayne
# Add Papertrail for log management
heroku addons:create papertrail:choklad
- Configure Redis caching:
# Add Redis
heroku addons:create heroku-redis:mini
# Set cache configuration
heroku config:set REDIS_URL=$(heroku config:get REDIS_TLS_URL)
- Set up AWS S3 for file storage:
heroku config:set AWS_ACCESS_KEY_ID=your-access-key
heroku config:set AWS_SECRET_ACCESS_KEY=your-secret-key
heroku config:set AWS_STORAGE_BUCKET_NAME=your-bucket-name
heroku config:set AWS_S3_REGION_NAME=your-region
- Enable SSL:
heroku config:set SECURE_SSL_REDIRECT=True
heroku config:set SECURE_PROXY_SSL_HEADER=True
- Set security headers:
heroku config:set SECURE_HSTS_SECONDS=31536000
heroku config:set SECURE_HSTS_INCLUDE_SUBDOMAINS=True
heroku config:set SECURE_HSTS_PRELOAD=True
- Scale web dynos:
# Scale to 2 dynos
heroku ps:scale web=2:basic
# Enable auto-scaling
heroku addons:create adept-scale:basic
- Database maintenance:
# Enable automatic backups
heroku pg:backups:schedule DATABASE_URL --at '02:00 UTC'
# Manual backup
heroku pg:backups:capture
# Download latest backup
heroku pg:backups:download
The application includes Django Health Check. Access health status at:
/health/
: Overall health status/health/db/
: Database connectivity/health/cache/
: Cache service status/health/storage/
: Storage service status
To enable maintenance mode:
heroku maintenance:on
To disable:
heroku maintenance:off
View application logs:
# View recent logs
heroku logs --tail
# Filter logs
heroku logs --source app --tail
heroku logs --source heroku --tail
# Search logs
heroku logs --grep "Error|Exception" --tail
- View application metrics:
# View dyno status
heroku ps
# View resource usage
heroku ps:utilization
# View response times
heroku logs:router --tail
- Database monitoring:
# View database status
heroku pg:info
# View database connections
heroku pg:ps
# View slow queries
heroku pg:outliers
- Database backups:
# Create manual backup
heroku pg:backups:capture
# Download backup
heroku pg:backups:download
# List backups
heroku pg:backups
# Restore from backup
heroku pg:backups:restore b101 DATABASE_URL
- Application rollback:
# View release history
heroku releases
# Rollback to previous release
heroku rollback v102
The following environment variables are required:
DJANGO_SECRET_KEY
: Django secret keyDJANGO_DEBUG
: Set to 'False' in productionASSEMBLYAI_API_KEY
: Your AssemblyAI API keyDJANGO_ALLOWED_HOSTS
: Comma-separated list of allowed hostsCORS_ALLOWED_ORIGINS
: Comma-separated list of allowed CORS originsDATABASE_URL
: Set automatically by Heroku PostgreSQL addon
Authentication is optional but recommended for higher rate limits. The API uses token-based authentication.
With Authentication:
curl -H "Authorization: Bearer YOUR_TOKEN" \
http://localhost:8000/api/transcribe/
Without Authentication:
curl http://localhost:8000/api/transcribe/
See Rate Limiting section for details on request limits.
To get higher rate limits, you can obtain an authentication token:
- Create a user account through the Django admin interface
- Generate a token for your user
- Include the token in your API requests using the Authorization header
- URL:
/api/transcribe/upload/
- Method:
POST
- Authentication: Optional
- Content-Type:
multipart/form-data
- Constraints:
- Maximum file size: 5MB
- Supported formats: MP3, WAV, M4A, AAC, OGG, FLAC
- Parameters:
file
: Audio file (required)language_code
: ISO language code (optional)auto_detect
: Boolean to enable language auto-detection (optional, default: true)
- Example:
curl -X POST \
-H "Authorization: Bearer your_token" \
-F "[email protected]" \
-F "language_code=en" \
http://localhost:8000/api/transcribe/upload/
- Response:
{
"message": "File uploaded and transcription started",
"transcript_id": "abc123xyz",
"status": "queued"
}
Endpoint: GET /api/transcribe/
List all transcriptions for the authenticated user, grouped by status. Results are paginated.
Parameters:
page
: Page number (default: 1)page_size
: Number of items per page (default: 10, max: 100)
Example Request:
curl -X GET \
'http://localhost:8000/api/transcribe/?page=1&page_size=10' \
-H 'Authorization: Bearer YOUR_TOKEN'
- Response:
{
"count": 15,
"next": "http://localhost:8000/api/transcribe/?page=2&page_size=10",
"previous": null,
"current_page": 1,
"total_pages": 2,
"total_count": 3,
"status_counts": {
"queued": 1,
"processing": 1,
"completed": 1,
"error": 0
},
"transcriptions": {
"queued": [{
"id": "abc123",
"text": null,
"audio_url": "https://example.com/audio1.mp3",
"language_code": "en",
"created_at": "2024-01-24T10:30:00Z",
"completed_at": null,
"error": null,
"status": "queued"
}],
"processing": [{
"id": "def456",
"text": null,
"audio_url": "https://example.com/audio2.mp3",
"language_code": "es",
"created_at": "2024-01-24T10:35:00Z",
"completed_at": null,
"error": null,
"status": "processing"
}],
"completed": [{
"id": "ghi789",
"text": "Your transcribed text here...",
"audio_url": "https://example.com/audio3.mp3",
"language_code": "en",
"created_at": "2024-01-24T10:25:00Z",
"completed_at": "2024-01-24T10:27:00Z",
"error": null,
"status": "completed"
}],
"error": []
}
}
The response includes:
count
: Total number of transcriptions across all pagesnext
: URL for the next page (null if on last page)previous
: URL for the previous page (null if on first page)current_page
: Current page numbertotal_pages
: Total number of pages availabletotal_count
: Number of transcriptions in the current responsestatus_counts
: Counts for each status in the current responsetranscriptions
: Grouped transcriptions for the current page
- URL:
/api/transcribe/{transcript_id}/
- Method:
GET
- Authentication: Optional
- Example:
curl -H "Authorization: Bearer your_token" \
http://localhost:8000/api/transcribe/abc123/
- Response:
{
"id": "abc123",
"status": "completed",
"text": "Your transcribed text here...",
"audio_url": "https://example.com/audio.mp3",
"language_code": "en",
"created_at": "2024-01-24T10:30:00Z",
"completed_at": "2024-01-24T10:32:00Z"
}
- URL:
/api/transcribe/
- Method:
GET
- Authentication: Optional
- Description: Returns all transcriptions in a single flat list, sorted by creation date (newest first)
- Example:
curl -H "Authorization: Bearer your_token" \
http://localhost:8000/api/transcribe/
- Response:
{
"count": 3,
"next": null,
"previous": null,
"results": [
{
"id": "abc123",
"text": "Your transcribed text here...",
"audio_url": "https://example.com/audio1.mp3",
"language_code": "en",
"created_at": "2024-01-24T10:30:00Z",
"completed_at": "2024-01-24T10:32:00Z",
"status": "completed",
"error": null
},
{
"id": "def456",
"text": null,
"audio_url": "https://example.com/audio2.mp3",
"language_code": "es",
"created_at": "2024-01-24T10:35:00Z",
"completed_at": null,
"status": "processing",
"error": null
}
]
}
The API implements rate limiting to ensure fair usage:
-
Authenticated Users:
- 25 requests per day
- Resets at midnight UTC
- Access to all transcriptions
- Full pagination support
- Rate limit headers included in response
-
Anonymous Users:
- 5 requests per day
- Resets at midnight UTC
- Limited to viewing 5 most recent transcriptions
- Same upload capabilities as authenticated users
- Consider authentication for full access
Rate Limit Response Headers:
X-RateLimit-Limit: 25
X-RateLimit-Remaining: 24
X-RateLimit-Reset: 1706745600
When rate limit is exceeded, the API returns:
{
"detail": "Request limit exceeded. Please try again tomorrow."
}
- Queued: The file has been uploaded and is waiting to be processed
- Processing: AssemblyAI is currently transcribing the audio
- Completed: Transcription is finished and text is available
- Error: An error occurred during transcription
The API returns appropriate HTTP status codes and error messages:
- 400: Bad Request (invalid input)
- 401: Unauthorized (invalid or missing token)
- 403: Forbidden (insufficient permissions)
- 404: Not Found
- 500: Internal Server Error
Error responses include detailed messages:
{
"error": "Error message here",
"details": "Additional error details if available"
}
- Maximum file size: 5MB
- Supported formats: MP3, WAV, M4A, and other common audio formats
- Clear audio quality recommended for best results
- Token-based authentication required for all endpoints
- File size validation
- Automatic temporary file cleanup
- Rate limiting on transcription requests
- Secure handling of API keys and tokens
To run tests:
python3 manage.py test
This project is licensed under the MIT License - see the LICENSE file for details.
The MIT License is a permissive license that is short and to the point. It lets people do anything they want with your code as long as they provide attribution back to you and don't hold you liable.
- ✓ Commercial use
- ✓ Modification
- ✓ Distribution
- ✓ Private use
- ✓ Liability limitations
- ✓ Warranty limitations
- License and copyright notice must be included with the code
- The same license must be used for derivatives