Back to Blog

How Do I Integrate JAMB Past Questions Into a React Native App?

If you're building a JAMB CBT app with React Native, one of the easiest ways to add structured JAMB/UTME past questions is by connecting your app to SdashAPI.

SdashAPI provides a REST API that returns JAMB questions, options, answers, solutions and examination information as JSON.

Your React Native app can fetch that data, store it in state and render the questions inside your own CBT interface.

The basic flow is:

React Native App
       ↓
Your Backend or API Request
       ↓
SdashAPI
       ↓
JAMB Questions in JSON
       ↓
Display Questions in Your CBT Screen

Here's how to set it up.

Step 1: Create a SdashAPI Account

Before your React Native app can fetch protected JAMB questions, you need a SdashAPI developer account.

After creating your account, go to your dashboard and get your personal Access Token.

Your token is used to authenticate requests.

It will be sent in the HTTP header like this:

AccessToken: YOUR_ACCESS_TOKEN

SdashAPI currently recommends using the HTTP header for authentication rather than exposing the token as a URL parameter in production applications.

Step 2: Test the JAMB API Endpoint

The core V1 question endpoint is:

https://sdashapi.com/api/v1/q

For example, to request JAMB Biology questions:

https://sdashapi.com/api/v1/q?subject=biology&type=utme

To request Biology questions from a particular year:

https://sdashapi.com/api/v1/q?subject=biology&type=utme&year=2022

And to request multiple questions:

https://sdashapi.com/api/v1/q?subject=biology&type=utme&year=2022&limit=20

SdashAPI supports filtering questions by subject, examination type and year, and V1 can return multiple questions in one request.

Step 3: Fetch JAMB Questions in React Native

React Native already supports fetch(), so you don't necessarily need an extra HTTP library.

Here is a simple example:

const fetchJambQuestions = async () => {
  try {
    const response = await fetch(
      "https://sdashapi.com/api/v1/q?subject=biology&type=utme&year=2022&limit=20",
      {
        headers: {
          AccessToken: "YOUR_ACCESS_TOKEN",
        },
      }
    );

    const result = await response.json();

    console.log(result);
  } catch (error) {
    console.error("Failed to fetch JAMB questions:", error);
  }
};

When the request succeeds, SdashAPI returns JSON that your React Native app can process.

Step 4: Store the Questions in State

Inside a React Native component, you can store the returned questions using useState.

import React, { useEffect, useState } from "react";
import {
  View,
  Text,
  TouchableOpacity,
  ActivityIndicator,
} from "react-native";

export default function JambPracticeScreen() {
  const [questions, setQuestions] = useState([]);
  const [loading, setLoading] = useState(true);

  const fetchQuestions = async () => {
    try {
      const response = await fetch(
        "https://sdashapi.com/api/v1/q?subject=biology&type=utme&year=2022&limit=20",
        {
          headers: {
            AccessToken: "YOUR_ACCESS_TOKEN",
          },
        }
      );

      const result = await response.json();

      if (result.status === 200) {
        setQuestions(
          Array.isArray(result.data)
            ? result.data
            : [result.data]
        );
      }
    } catch (error) {
      console.error(error);
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => {
    fetchQuestions();
  }, []);

  if (loading) {
    return <ActivityIndicator size="large" />;
  }

  return (
    <View>
      <Text>Questions loaded: {questions.length}</Text>
    </View>
  );
}

The Array.isArray() check is useful because your code can safely handle either a single question object or an array of questions.

What Does the SdashAPI Response Look Like?

A V1 question response can include fields such as:

{
  "status": 200,
  "data": {
    "id": 4821,
    "question": "Which of the following is the chemical formula for table salt?",
    "section": null,
    "option": {
      "a": "NaCl",
      "b": "KCl",
      "c": "CaCO3",
      "d": "NaOH"
    },
    "answer": "a",
    "solution": "NaCl is sodium chloride.",
    "image": null,
    "examtype": "UTME",
    "examyear": "2022"
  }
}

The core V1 response provides the information needed for a traditional CBT experience, including the question, options, correct answer, solution, examination type and year.

Step 5: Display One JAMB Question

Now you can display the first question.

const question = questions[0];

return (
  <View style={{ padding: 20 }}>
    <Text style={{ fontSize: 18, marginBottom: 20 }}>
      {question.question}
    </Text>
  </View>
);

For a full CBT app, you'll normally track the current question index.

const [currentIndex, setCurrentIndex] = useState(0);

const currentQuestion = questions[currentIndex];

Then your screen can display:

<Text>
  Question {currentIndex + 1} of {questions.length}
</Text>

<Text>
  {currentQuestion.question}
</Text>

Step 6: Display the Answer Options

SdashAPI V1 returns options as an object.

For example:

{
  "a": "NaCl",
  "b": "KCl",
  "c": "CaCO3",
  "d": "NaOH"
}

You can convert this object into an array using:

const options = Object.entries(currentQuestion.option);

That produces values like:

[
  ["a", "NaCl"],
  ["b", "KCl"],
  ["c", "CaCO3"],
  ["d", "NaOH"]
]

Now render them:

{Object.entries(currentQuestion.option).map(
  ([key, value]) => (
    <TouchableOpacity
      key={key}
      onPress={() => selectAnswer(key)}
      style={{
        padding: 15,
        marginBottom: 10,
        borderWidth: 1,
        borderRadius: 8,
      }}
    >
      <Text>
        {key.toUpperCase()}. {value}
      </Text>
    </TouchableOpacity>
  )
)}

Now you have a basic working JAMB CBT question screen.

Step 7: Save the Student's Selected Answer

You need to remember what the student selects for each question.

One simple approach is to use an object:

const [answers, setAnswers] = useState({});

Then:

const selectAnswer = (option) => {
  setAnswers((previousAnswers) => ({
    ...previousAnswers,
    [currentQuestion.id]: option,
  }));
};

Your answers object could eventually look like:

{
  4821: "a",
  4822: "c",
  4823: "b"
}

Each question ID maps to the option selected by the student.

Step 8: Add Previous and Next Buttons

A CBT app needs question navigation.

const nextQuestion = () => {
  if (currentIndex < questions.length - 1) {
    setCurrentIndex(currentIndex + 1);
  }
};

const previousQuestion = () => {
  if (currentIndex > 0) {
    setCurrentIndex(currentIndex - 1);
  }
};

Then render:

<View
  style={{
    flexDirection: "row",
    justifyContent: "space-between",
    marginTop: 20,
  }}
>
  <TouchableOpacity onPress={previousQuestion}>
    <Text>Previous</Text>
  </TouchableOpacity>

  <TouchableOpacity onPress={nextQuestion}>
    <Text>Next</Text>
  </TouchableOpacity>
</View>

Your CBT navigation is now functional.

Step 9: Mark the JAMB Test

When the student submits, compare their selected answers with the correct answers from the API.

const calculateScore = () => {
  let score = 0;

  questions.forEach((question) => {
    const selectedAnswer = answers[question.id];

    if (selectedAnswer === question.answer) {
      score++;
    }
  });

  return score;
};

For example, if the student gets 16 out of 20:

Score: 16/20

You could calculate a percentage:

const percentage =
  (calculateScore() / questions.length) * 100;

and display:

80%

Step 10: Show Solutions After Submission

Because SdashAPI can provide the correct answer and solution, you can build a correction screen.

For example:

<Text>
  Correct Answer:
  {currentQuestion.answer.toUpperCase()}
</Text>

<Text>
  {currentQuestion.solution}
</Text>

This turns your CBT app into a revision tool instead of simply giving students their final score.

Build a Subject Selection Screen

You probably don't want Biology permanently hardcoded into the API URL.

Instead, create a subject selection screen.

For example:

const subjects = [
  "english",
  "mathematics",
  "biology",
  "chemistry",
  "physics",
  "economics",
  "government",
];

Then when a student selects Biology:

setSelectedSubject("biology");

Your API request becomes:

const url =
  `https://sdashapi.com/api/v1/q` +
  `?subject=${selectedSubject}` +
  `&type=utme` +
  `&limit=40`;

Now one screen can support several JAMB subjects.

Add Examination-Year Selection

You can do the same thing with the year.

const [selectedYear, setSelectedYear] =
  useState("2022");

Then:

const url =
  `https://sdashapi.com/api/v1/q` +
  `?subject=${selectedSubject}` +
  `&type=utme` +
  `&year=${selectedYear}` +
  `&limit=40`;

The resulting request could be:

/api/v1/q?subject=physics&type=utme&year=2022&limit=40

SdashAPI's current documentation supports these kinds of subject, exam-type and year filters.

Add a CBT Timer

The examination timer is something your React Native application should manage locally.

For example:

const [timeLeft, setTimeLeft] = useState(1800);

That gives you 1,800 seconds, or 30 minutes.

You can decrement it every second using setInterval.

useEffect(() => {
  if (timeLeft <= 0) {
    submitExam();
    return;
  }

  const timer = setInterval(() => {
    setTimeLeft((time) => time - 1);
  }, 1000);

  return () => clearInterval(timer);
}, [timeLeft]);

SdashAPI supplies the question content while your application controls the exam experience.

What Should SdashAPI Handle?

SdashAPI can handle the educational content layer.

For example:

Questions
Options
Answers
Solutions
Subjects
Years
Question metadata

Your React Native application handles things like:

User accounts
CBT UI
Timers
Navigation
Selected answers
Scores
Subscriptions
Bookmarks
Progress tracking
Analytics
Offline state

That separation makes your application easier to maintain.

Don't Expose Your Access Token in Production

This part is important.

You may see examples like:

headers: {
  AccessToken: "YOUR_ACCESS_TOKEN"
}

That is useful for development and testing.

However, hardcoding your private production SdashAPI Access Token directly into a distributed React Native application can expose it to users who inspect the app.

SdashAPI itself recommends keeping private credentials on your backend where possible.

A better production architecture is:

React Native App
       ↓
Your Backend
       ↓
SdashAPI

For example:

React Native App

GET https://yourdomain.com/api/questions
        ↓

Your Node.js/Laravel/PHP Backend
        ↓

GET https://sdashapi.com/api/v1/q
AccessToken: PRIVATE_TOKEN
        ↓

SdashAPI

Your Access Token remains on your server.

Example Backend Flow

Your React Native application might request:

fetch(
  "https://yourdomain.com/api/jamb/questions?subject=biology&year=2022"
);

Your backend then makes the actual SdashAPI request using your private token.

This also allows your backend to add:

  • User authentication
  • Subscription checks
  • Rate limiting
  • Caching
  • Usage tracking
  • Custom limits
  • Anti-abuse controls

before returning the questions to your app.

Can I Use Expo With SdashAPI?

Yes.

If your React Native application is built using Expo, you can still make normal HTTP requests with fetch().

You don't need a special native module just to communicate with SdashAPI.

A basic Expo app can therefore request your questions just like any other React Native application.

Can I Build a Complete JAMB CBT App With SdashAPI?

Yes.

A basic project could contain screens like:

Home
   ↓
Select Subject
   ↓
Select Year
   ↓
CBT Instructions
   ↓
Questions
   ↓
Submit Exam
   ↓
Result
   ↓
Corrections

SdashAPI supplies the examination content while your React Native app handles the interface and user experience.

What About Advanced JAMB Features?

SdashAPI is not limited to the core V1 question endpoint.

Its current API structure includes several versions aimed at different types of educational applications.

V1: Core Questions

Useful for standard CBT functionality.

You get the essential information required to present and mark examination questions.

V2: Metadata and Taxonomy

Useful for organising questions around additional information such as topics and subtopics.

V3: Detailed Explanations

Useful for revision applications that want to provide more educational feedback.

V4: Related Question Intelligence

Useful for adaptive learning features where your app recommends similar questions after a learner struggles with a particular concept.

This means your first version could simply be a CBT application, while later versions could introduce personalised practice and deeper learning tools.

Can I Test the Integration for Free?

Yes.

SdashAPI provides developers with a Sandbox for testing integrations.

The Sandbox includes 2,000 API credits, which you can use while building and testing your React Native application.

This gives you room to test things like:

  • API authentication
  • Fetch requests
  • JSON parsing
  • Subject selection
  • Year selection
  • CBT navigation
  • Answer selection
  • Scoring
  • Error handling

before moving your application into full production use.

Complete React Native Example

Here is a simplified version bringing the core pieces together:

import React, { useEffect, useState } from "react";
import {
  View,
  Text,
  TouchableOpacity,
  ActivityIndicator,
} from "react-native";

export default function JambCBT() {
  const [questions, setQuestions] = useState([]);
  const [currentIndex, setCurrentIndex] =
    useState(0);
  const [answers, setAnswers] = useState({});
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    loadQuestions();
  }, []);

  const loadQuestions = async () => {
    try {
      const response = await fetch(
        "https://sdashapi.com/api/v1/q?subject=biology&type=utme&year=2022&limit=20",
        {
          headers: {
            AccessToken: "YOUR_ACCESS_TOKEN",
          },
        }
      );

      const result = await response.json();

      if (result.status === 200) {
        const questionData = Array.isArray(result.data)
          ? result.data
          : [result.data];

        setQuestions(questionData);
      }
    } catch (error) {
      console.error(error);
    } finally {
      setLoading(false);
    }
  };

  const selectAnswer = (option) => {
    const question = questions[currentIndex];

    setAnswers((previous) => ({
      ...previous,
      [question.id]: option,
    }));
  };

  const calculateScore = () => {
    let score = 0;

    questions.forEach((question) => {
      if (answers[question.id] === question.answer) {
        score++;
      }
    });

    return score;
  };

  if (loading) {
    return <ActivityIndicator size="large" />;
  }

  if (!questions.length) {
    return <Text>No questions found.</Text>;
  }

  const currentQuestion =
    questions[currentIndex];

  return (
    <View style={{ padding: 20 }}>
      <Text>
        Question {currentIndex + 1} of{" "}
        {questions.length}
      </Text>

      <Text
        style={{
          fontSize: 18,
          marginVertical: 20,
        }}
      >
        {currentQuestion.question}
      </Text>

      {Object.entries(currentQuestion.option).map(
        ([key, value]) => (
          <TouchableOpacity
            key={key}
            onPress={() => selectAnswer(key)}
            style={{
              padding: 15,
              marginBottom: 10,
              borderWidth: 1,
              borderRadius: 8,
            }}
          >
            <Text>
              {key.toUpperCase()}. {value}
            </Text>
          </TouchableOpacity>
        )
      )}

      <TouchableOpacity
        onPress={() =>
          setCurrentIndex((index) =>
            Math.min(
              index + 1,
              questions.length - 1
            )
          )
        }
      >
        <Text>Next Question</Text>
      </TouchableOpacity>

      <TouchableOpacity
        onPress={() =>
          console.log(
            "Score:",
            calculateScore()
          )
        }
      >
        <Text>Submit Test</Text>
      </TouchableOpacity>
    </View>
  );
}

For a real production app, move the private SdashAPI Access Token to your backend rather than shipping it inside this component.

How Do I Integrate JAMB Past Questions Into a React Native App?

In summary:

  1. Create a SdashAPI developer account.
  2. Get your Access Token.
  3. Request JAMB questions from /api/v1/q.
  4. Use type=utme.
  5. Add subject, year and limit filters.
  6. Parse the returned JSON in React Native.
  7. Store the questions in state.
  8. Render the question and answer options.
  9. Record the student's selected answers.
  10. Add previous and next navigation.
  11. Calculate the final score.
  12. Display corrections and solutions.
  13. Keep your production Access Token on your backend.

A request can be as simple as:

const response = await fetch(
  "https://sdashapi.com/api/v1/q?subject=biology&type=utme&year=2022&limit=20",
  {
    headers: {
      AccessToken: "YOUR_ACCESS_TOKEN",
    },
  }
);

const data = await response.json();

From there, the rest of the CBT experience is entirely yours to build.

SdashAPI handles the structured JAMB examination content while your React Native application handles the interface, user experience and learning features.

Avatar

How can we help?

We reply immediately

Hello! 👋 How can we help you today?