How do I make WordPress use a template for any requests where the url is /careers/[0-9]+?

We used to have custom posts for job openings handled by single-jobs.php - then switched to an Applicant Tracking System with a nice REST API, meaning no more job posts in the data base on our end.

I considered making a single job page with a query string for the parameter for the 3rd party API (bla.com/careers/job?id=123). A page-job.php template would then read the parameter and use to request data from the ATS API and then echo a page accordingly.

But for SEO reasons, I want the routing to have "proper" slugs such as bla.com/careers/123.

Would I need to basically do what I suggested above except use JavaScript to change/fake what the url looks like, or is there a backend way of doing that?

How do I make that happen?


Conversation with Howdy_McGee in chat revealed that what I'm looking for (content is not managed via the WordPress dashboard but fits right in) is called a "virtual page" according to a medium article.


2 Answers 2


The easiest way is to simply create a custom rewrite endpoint. This is a two-fer. It will create our actual permalink structure and our query var in one go.

Do note that after creating the below you will need to resave permalinks: Settings -> Permalinks -> save button.

 * Create Careers Endpoint
 * @return void
function prefix_careers_endpoint() {

        'careers',  // Permalink Endpoint
        EP_PAGES,   // Use `EP_ROOT` for more complex permalink strucures such as `careers/job`
        'jobid'     // Custom Query Var ( if not passed, defaults to Endpoint - first parameter `careers` )

add_action( 'init', 'prefix_careers_endpoint' );

Next we can check the global $wp_query object for the query var's existence. If it exists we'll switch out the current template with one of our own choosing. In this case it will look for in the theme endpoint-careers.php.

 * Include template if query var exists
 * @param String $template
 * @return String $template
function prefix_careers_template( $template ) {

    global $wp_query;

    // Ensure our `jobid` query var exists and is not empty
    if( '' !== $wp_query->get( 'jobid' ) ) {

        // Ensure our template file exists
        if( '' !== ( $new_template = locate_template( 'endpoint-careers.php' ) ) ) {
            $template = $new_template; // Assign templae file path from conditional


    return $template;

add_filter( 'template_include', 'prefix_careers_template' );

Finally, inside our custom template file endpoint-careers.php after get_header() we can create and call a custom function prefix_get_data() which will grab the jobid from the global $wp_query and do a wp_remote_get() against your API passing in the Job ID. This will hopefully return useful data we can then use in the template.

 * Return data from external API
 * @param Integer $jobid
 * @return Array
function prefix_get_data( $jobid = 0 ) {

    global $wp_query;

    $jobid = ( ! empty( $jobid ) ) ? $wp_query->get( 'jobid', $jobid ) : $jobid;

    $response = wp_remote_get( 'http://foobar.url', array(
        'body' => array(
            'jobid' => $jobid,
    ) );

    return json_decode( $response );


Calling it as $data = prefix_get_data(). You may need to modify this function depending on what your API expects to get and what the API returns. You could even create a custom class with with intuitive methods to handle and call your data. The possibilities are endless!


The easiest backend way of receiving a url like /careers/123 internally processing it as /careers/job?position_id=123 and return those contents, without actually re-directing / changing the url is a "url rewrite". I thought initially those were just for file names but they're actually quite powerful.

This did it for me:

    add_action( 'init', 'job_rewrite' );

    function job_rewrite() {
        global $wp;
        global $wp_rewrite;
        $rule = '^careers/(\d+)/?$';
        $job_page_id = [job-page id here];
        $rewrite = 'index.php?page_id=' . $job_page_id . '&position_id=$matches[1]'; //job/?id=
        add_rewrite_rule( $rule, $rewrite, 'top' );

Then all I had to do was handle the API request in the page-job.php template file.

global $wp;
$position_id = $wp->query_vars['position_id'];
// handle the API request with the ID

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.