Drupal 7 - HTTP/AJAX/JSON responses at same URL with Accept Header (like D8)
I am not sure whether this is clean or "best practice" but I am currently using something like this:

  // If the request has the header 'Accept: application/json'
  // return the output in JSON format and stop.
  if ($_SERVER['HTTP_ACCEPT'] == 'application/json') {
    drupal_add_http_header('Content-Type', 'application/json; utf-8');
    print json_encode($strings);
    exit;
  }


To see it in the context, here is the whole function:

&lt;?php
/**
 * Menu callback: translations/search.
 *
 * Search strings and translations using various filters.
 *
 * If the request header 'Accept: application/json' is set, then
 * the output will be in JSON, otherwise it will be an HTML page.
 * For example:
 *   curl -k -i -H 'Accept: application/json' \
 *        'https://dev.l10n.org.xx/translations/search?lng=sq&amp;words=space'
 */
function btrClient_search() {
  // Get the search parameters from the GET request.
  $params = btrClient_get_search_params();

  // Get the strings that match the search params.
  $btr = btr_user_is_authenticated() ?
    wsclient_service_load('btr') :
    wsclient_service_load('public_btr');
  $result = $btr-&gt;search($params);
  $filter = $result['filter'];
  $pager = $result['pager'];
  $strings = $result['strings'];

  // If the request has the header 'Accept: application/json'
  // return the output in JSON format and stop.
  if ($_SERVER['HTTP_ACCEPT'] == 'application/json') {
    drupal_add_http_header('Content-Type', 'application/json; utf-8');
    print json_encode($strings);
    exit;
  }

  // Add the CSS and JS files.
  drupal_add_css(drupal_get_path('module', 'btrClient') . '/editor/editor.css');
  drupal_add_js(drupal_get_path('module', 'btrClient') . '/editor/jquery.worddiff.js');
  drupal_add_js(drupal_get_path('module', 'btrClient') . '/editor/editor.js');

  // Add RTL style if the current language's direction is RTL.
  $languages = btrClient_get_languages();
  $lng = btrClient_get_translation_lng();
  if ($languages[$lng]['direction'] == LANGUAGE_RTL) {
    drupal_add_css(drupal_get_path('module', 'btrClient') . '/editor-rtl.css');
  }

  // Set the title.
  drupal_set_title(t('Search Translations'));

  // Add the filter form.
  $output = array(
    drupal_get_form('btrClient_filter_form', $filter),
  );

  // Output the strings.
  if (empty($strings)) {
    drupal_set_message(t('No strings found. Try adjusting the filter options.'));
  }
  else {
    pager_default_initialize($pager['number_of_items'], $pager['items_per_page']);
    $output[] = drupal_get_form('btrClient_translate_form', $strings, $lng);
  }

  return $output;
}


(Or check it out here: http://cgit.drupalcode.org/btrclient/tree/includes/search.inc