diff --git a/requirements.txt b/requirements.txt
index 9732a525804ca350a44d28a76c17d3a0fd8ce986..4edebd6b0646bd3afbc9d5f613964dea13a26e24 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,5 @@
 Django==5.0.11
+django-rest-framework
 ruff
 mypy
 tox
diff --git a/sage_validation/file_validator/templates/upload.html b/sage_validation/file_validator/templates/upload.html
index 4a595da4794ec9281508073a490bc54daa4549f5..ec8619dd40130d1555c59dc7a549c6c359c57edc 100644
--- a/sage_validation/file_validator/templates/upload.html
+++ b/sage_validation/file_validator/templates/upload.html
@@ -40,21 +40,18 @@
         </div>
     </div>
 
-
     <script>
-        const form = document.getElementById('uploadForm');
-        const fileInput = document.getElementById('fileInput');
-        const errorSection = document.getElementById('errorSection');
-        const errorList = document.getElementById('errorList');
-        const successSection = document.getElementById('successSection');
-        const successMessage = document.getElementById('successMessage');
-        const downloadSection = document.getElementById('downloadSection');
-        const downloadLink = document.getElementById('downloadLink');
-
-        form.addEventListener('submit', async function (e) {
+        document.getElementById('uploadForm').addEventListener('submit', async function (e) {
             e.preventDefault();
 
-            // Clear previous messages
+            const fileInput = document.getElementById('fileInput');
+            const errorSection = document.getElementById('errorSection');
+            const errorList = document.getElementById('errorList');
+            const successSection = document.getElementById('successSection');
+            const successMessage = document.getElementById('successMessage');
+            const downloadSection = document.getElementById('downloadSection');
+            const downloadLink = document.getElementById('downloadLink');
+
             errorList.innerHTML = '';
             successMessage.innerHTML = '';
             errorSection.classList.add('hidden');
@@ -65,7 +62,7 @@
             formData.append('file', fileInput.files[0]);
 
             try {
-                const response = await fetch('', {
+                const response = await fetch("{% url 'upload-file' %}", {
                     method: 'POST',
                     body: formData,
                     headers: {
@@ -81,38 +78,13 @@
                     downloadLink.href = result.download_url;
                     downloadSection.classList.remove('hidden');
                 } else if (response.status === 400 && result.status === 'error') {
-                    errorList.innerHTML = '';
-
-                    if (Array.isArray(result.errors)) {
-                        result.errors.forEach(errorObj => {
-                            if (typeof errorObj === 'string') {
-                                const li = document.createElement('li');
-                                li.textContent = errorObj;
-                                errorList.appendChild(li);
-                            } else {
-                                for (const [field, messages] of Object.entries(errorObj)) {
-                                    messages.forEach(message => {
-                                        const li = document.createElement('li');
-                                        li.textContent = `${field}: ${message}`;
-                                        errorList.appendChild(li);
-                                    });
-                                }
-                            }
+                    for (const [field, messages] of Object.entries(result.errors)) {
+                        messages.forEach(message => {
+                            const li = document.createElement('li');
+                            li.textContent = `${field}: ${message}`;
+                            errorList.appendChild(li);
                         });
-                    } else if (typeof result.errors === 'object') {
-                        for (const [field, messages] of Object.entries(result.errors)) {
-                            messages.forEach(message => {
-                                const li = document.createElement('li');
-                                li.textContent = `${field}: ${message}`;
-                                errorList.appendChild(li);
-                            });
-                        }
-                    } else {
-                        const li = document.createElement('li');
-                        li.textContent = result.errors;
-                        errorList.appendChild(li);
                     }
-
                     errorSection.classList.remove('hidden');
                 }
             } catch (error) {
diff --git a/sage_validation/file_validator/urls.py b/sage_validation/file_validator/urls.py
index a5c0d9fd7840097233aabed1184a392037d7bcc1..ee5ae78157bbb92dcb77a93df310e41755f46739 100644
--- a/sage_validation/file_validator/urls.py
+++ b/sage_validation/file_validator/urls.py
@@ -2,9 +2,10 @@
 
 from django.urls import path
 
-from sage_validation.file_validator.views import CSVExportView, CSVUploadView
+from sage_validation.file_validator.views import CSVExportAPIView, CSVUploadAPIView, upload_page_view
 
 urlpatterns = [
-    path("upload/", CSVUploadView.as_view(), name="upload-file"),
-    path("export/", CSVExportView.as_view(), name="export-file"),
+    path("upload-page/", upload_page_view, name="upload-page"),
+    path("api/upload/", CSVUploadAPIView.as_view(), name="upload-file"),
+    path("api/export/", CSVExportAPIView.as_view(), name="export-file"),
 ]
diff --git a/sage_validation/file_validator/views.py b/sage_validation/file_validator/views.py
index 99efd175b0fa9498eb15127578cd85e272e21ac1..bfa80c2cde6e71b61c4849f7183a669d74b739c2 100644
--- a/sage_validation/file_validator/views.py
+++ b/sage_validation/file_validator/views.py
@@ -1,14 +1,15 @@
 """Views for the file_validator app."""
 import csv
 import io
-from typing import Any
 
-from django.http import HttpRequest, HttpResponse, JsonResponse
+from django.http import HttpRequest, HttpResponse
 from django.shortcuts import render
 from django.urls import reverse_lazy
 from django.utils import timezone
-from django.views.generic.base import View
-from django.views.generic.edit import FormView
+from rest_framework import status
+from rest_framework.request import Request
+from rest_framework.response import Response
+from rest_framework.views import APIView
 
 from sage_validation.file_validator.forms import CSVUploadForm
 from sage_validation.file_validator.models import MeoCostCentres, XxData
@@ -19,46 +20,41 @@ def index_view(request: HttpRequest) -> HttpResponse:
     return render(request, "index.html")
 
 
-class CSVUploadView(FormView):
-    """View for uploading a CSV file."""
+def upload_page_view(request: HttpRequest) -> HttpResponse:
+    """Render the file upload page."""
+    return render(request, "upload.html")
 
-    template_name = "upload.html"
-    form_class = CSVUploadForm
-    success_url = reverse_lazy("upload-file")
 
-    def get_context_data(self, **kwargs: dict[str, Any]) -> dict[str, Any]:
-        """Render the form with no error message on GET request."""
-        context = super().get_context_data(**kwargs)
-        context["error"] = None
-        context["message"] = None
-        return context
+class CSVUploadAPIView(APIView):
+    """API view for uploading a CSV file."""
+
+    def post(self, request: Request) -> Response:
+        """Handle CSV upload and validation."""
+        form = CSVUploadForm(data=request.data, files=request.FILES)
+
+        if not form.is_valid():
+            return Response({"status": "error", "errors": form.errors}, status=status.HTTP_400_BAD_REQUEST)
 
-    def form_valid(self, form: CSVUploadForm) -> JsonResponse:
-        """Handle the CSV validation, store valid data, and prepare for export."""
         csv_file = form.cleaned_data["file"]
         csv_file.seek(0)
         decoded_file = csv_file.read().decode("utf-8").strip()
 
         if not decoded_file:
-            return JsonResponse({"status": "error", "message": "Uploaded file is empty."}, status=400)
+            return Response({"status": "error", "message": "Uploaded file is empty."},
+                            status=status.HTTP_400_BAD_REQUEST)
 
         reader = csv.DictReader(io.StringIO(decoded_file))
         csv_data: list[dict[str, str]] = list(reader)
 
         updated_data = self.update_fields(csv_data)
+        request.session["validated_csv"] = updated_data
+        request.session.modified = True
 
-        self.request.session["validated_csv"] = updated_data
-        self.request.session.modified = True
-
-        return JsonResponse({
+        return Response({
             "status": "success",
             "message": "File successfully uploaded and processed.",
             "download_url": reverse_lazy("export-file")
-        })
-
-    def form_invalid(self, form: CSVUploadForm) -> JsonResponse:
-        """Handle the form when it is invalid."""
-        return JsonResponse({"status": "error", "errors": form.errors}, status=400)
+        }, status=status.HTTP_200_OK)
 
     @staticmethod
     def update_fields(csv_data: list[dict[str, str]]) -> list[dict[str, str]]:
@@ -91,21 +87,21 @@ class CSVUploadView(FormView):
                     row[f"NominalAnalysisNominalAccountNumber/{repeat}"] = (
                         xx_data[0] if cc_type == "Project" else xx_data[1]
                     )
-
                 repeat += 1
 
         return csv_data
 
 
-class CSVExportView(View):
-    """View for exporting the updated CSV file."""
+class CSVExportAPIView(APIView):
+    """API view for exporting the updated CSV file."""
 
-    def get(self, request: HttpRequest) -> HttpResponse:
-        """Generate a downloadable CSV file with updated values."""
+    def get(self, request: Request) -> Response:
+        """Return processed CSV as a downloadable response."""
         csv_data: list[dict[str, str]] = request.session.get("validated_csv", [])
 
         if not csv_data:
-            return HttpResponse("No data available for export.", status=400)
+            return Response({"status": "error", "message": "No data available for export."},
+                            status=status.HTTP_400_BAD_REQUEST)
 
         response = HttpResponse(content_type="text/csv")
         response["Content-Disposition"] = 'attachment; filename="updated_file.csv"'
diff --git a/sage_validation/templates/index.html b/sage_validation/templates/index.html
index 7d7376ea866409f6da728b1c6a343fa98b8fb7a4..72db6b8b5598643485a9e64dcfd7d9b10aa1f65b 100644
--- a/sage_validation/templates/index.html
+++ b/sage_validation/templates/index.html
@@ -8,7 +8,7 @@
         <h1 class="text-5xl md:text-6xl font-bold mb-12 text-blue-900">Welcome to Sage Validation</h1>
         <p class="text-xl md:text-2xl mb-16 text-gray-700">Click the button below to upload your file for validation.</p>
 
-        <a href="{% url "upload-file" %}"
+        <a href="{% url "upload-page" %}"
            class="inline-flex py-4 px-16 bg-blue-600 text-white text-lg md:text-xl font-bold rounded-full shadow-lg transition-transform transform hover:scale-105 focus:outline-none focus:ring-4 focus:ring-blue-300 focus:ring-opacity-50">
             Upload File
         </a>